1use std::{
2 cmp::Ordering,
3 fmt::Display,
4 ops::{Index, IndexMut},
5 slice::{Iter, IterMut},
6 str::FromStr,
7};
8
9#[cfg(feature = "span")]
10use miette::SourceSpan;
11
12use crate::{
13 v2_parser, FormatConfig, KdlDocument, KdlDocumentFormat, KdlEntry, KdlError, KdlIdentifier,
14 KdlValue,
15};
16
17#[derive(Debug, Clone, Eq)]
21pub struct KdlNode {
22 pub(crate) ty: Option<KdlIdentifier>,
23 pub(crate) name: KdlIdentifier,
24 pub(crate) entries: Vec<KdlEntry>,
26 pub(crate) children: Option<KdlDocument>,
27 pub(crate) format: Option<KdlNodeFormat>,
28 #[cfg(feature = "span")]
29 pub(crate) span: SourceSpan,
30}
31
32impl PartialEq for KdlNode {
33 fn eq(&self, other: &Self) -> bool {
34 self.ty == other.ty
35 && self.name == other.name
36 && self.entries == other.entries
37 && self.children == other.children
38 && self.format == other.format
39 }
41}
42
43impl std::hash::Hash for KdlNode {
44 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
45 self.ty.hash(state);
46 self.name.hash(state);
47 self.entries.hash(state);
48 self.children.hash(state);
49 self.format.hash(state);
50 }
52}
53
54impl KdlNode {
55 pub fn new(name: impl Into<KdlIdentifier>) -> Self {
57 Self {
58 name: name.into(),
59 ty: None,
60 entries: Vec::new(),
61 children: None,
62 format: Some(KdlNodeFormat {
63 trailing: "\n".into(),
64 ..Default::default()
65 }),
66 #[cfg(feature = "span")]
67 span: SourceSpan::from(0..0),
68 }
69 }
70
71 pub fn name(&self) -> &KdlIdentifier {
73 &self.name
74 }
75
76 pub fn name_mut(&mut self) -> &mut KdlIdentifier {
78 &mut self.name
79 }
80
81 pub fn set_name(&mut self, name: impl Into<KdlIdentifier>) {
83 self.name = name.into();
84 }
85
86 #[cfg(feature = "span")]
92 pub fn span(&self) -> SourceSpan {
93 self.span
94 }
95
96 #[cfg(feature = "span")]
98 pub fn set_span(&mut self, span: impl Into<SourceSpan>) {
99 self.span = span.into();
100 }
101
102 pub fn ty(&self) -> Option<&KdlIdentifier> {
104 self.ty.as_ref()
105 }
106
107 pub fn ty_mut(&mut self) -> &mut Option<KdlIdentifier> {
109 &mut self.ty
110 }
111
112 pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
114 self.ty = Some(ty.into());
115 }
116
117 pub fn entries(&self) -> &[KdlEntry] {
119 &self.entries
120 }
121
122 pub fn entries_mut(&mut self) -> &mut Vec<KdlEntry> {
125 &mut self.entries
126 }
127
128 pub fn clear_format(&mut self) {
135 self.format = None;
136 }
137
138 pub fn clear_format_recursive(&mut self) {
142 self.clear_format();
143 self.name.clear_format();
144 if let Some(children) = &mut self.children {
145 children.clear_format_recursive();
146 }
147 for entry in self.entries.iter_mut() {
148 entry.clear_format();
149 }
150 }
151
152 pub fn entry(&self, key: impl Into<NodeKey>) -> Option<&KdlEntry> {
155 self.entry_impl(key.into())
156 }
157
158 fn entry_impl(&self, key: NodeKey) -> Option<&KdlEntry> {
159 match key {
160 NodeKey::Key(key) => {
161 let mut current = None;
162 for entry in &self.entries {
163 if entry.name.is_some()
164 && entry.name.as_ref().map(|i| i.value()) == Some(key.value())
165 {
166 current = Some(entry);
167 }
168 }
169 current
170 }
171 NodeKey::Index(idx) => {
172 let mut current_idx = 0;
173 for entry in &self.entries {
174 if entry.name.is_none() {
175 if current_idx == idx {
176 return Some(entry);
177 }
178 current_idx += 1;
179 }
180 }
181 None
182 }
183 }
184 }
185
186 pub fn entry_mut(&mut self, key: impl Into<NodeKey>) -> Option<&mut KdlEntry> {
189 self.entry_mut_impl(key.into())
190 }
191
192 fn entry_mut_impl(&mut self, key: NodeKey) -> Option<&mut KdlEntry> {
193 match key {
194 NodeKey::Key(key) => {
195 let mut current = None;
196 for entry in &mut self.entries {
197 if entry.name.is_some()
198 && entry.name.as_ref().map(|i| i.value()) == Some(key.value())
199 {
200 current = Some(entry);
201 }
202 }
203 current
204 }
205 NodeKey::Index(idx) => {
206 let mut current_idx = 0;
207 for entry in &mut self.entries {
208 if entry.name.is_none() {
209 if current_idx == idx {
210 return Some(entry);
211 }
212 current_idx += 1;
213 }
214 }
215 None
216 }
217 }
218 }
219
220 pub fn children(&self) -> Option<&KdlDocument> {
222 self.children.as_ref()
223 }
224
225 pub fn children_mut(&mut self) -> &mut Option<KdlDocument> {
227 &mut self.children
228 }
229
230 pub fn set_children(&mut self, children: KdlDocument) {
232 self.children = Some(children);
233 }
234
235 pub fn clear_children(&mut self) {
237 self.children = None;
238 }
239
240 pub fn ensure_children(&mut self) -> &mut KdlDocument {
243 if self.children.is_none() {
244 self.children = Some(KdlDocument::new());
245 }
246 self.children_mut().as_mut().unwrap()
247 }
248
249 pub fn format(&self) -> Option<&KdlNodeFormat> {
251 self.format.as_ref()
252 }
253
254 pub fn format_mut(&mut self) -> Option<&mut KdlNodeFormat> {
256 self.format.as_mut()
257 }
258
259 pub fn set_format(&mut self, format: KdlNodeFormat) {
261 self.format = Some(format);
262 }
263 pub fn autoformat(&mut self) {
265 self.autoformat_config(&FormatConfig::default());
266 }
267
268 pub fn autoformat_no_comments(&mut self) {
270 self.autoformat_config(&FormatConfig {
271 no_comments: true,
272 ..Default::default()
273 });
274 }
275
276 pub fn autoformat_config(&mut self, config: &FormatConfig<'_>) {
278 if let Some(KdlNodeFormat {
279 leading,
280 before_terminator,
281 terminator,
282 trailing,
283 before_children,
284 ..
285 }) = self.format_mut()
286 {
287 crate::fmt::autoformat_leading(leading, config);
288 crate::fmt::autoformat_trailing(before_terminator, config.no_comments);
289 crate::fmt::autoformat_trailing(trailing, config.no_comments);
290 *trailing = trailing.trim().into();
291 if !terminator.starts_with('\n') {
292 *terminator = "\n".into();
293 }
294 if let Some(c) = trailing.chars().next() {
295 if !c.is_whitespace() {
296 trailing.insert(0, ' ');
297 }
298 }
299
300 *before_children = " ".into();
301 } else {
302 self.set_format(KdlNodeFormat {
303 terminator: "\n".into(),
304 ..Default::default()
305 })
306 }
307 self.name.clear_format();
308 if let Some(ty) = self.ty.as_mut() {
309 ty.clear_format()
310 }
311 for entry in &mut self.entries {
312 if config.entry_autoformate_keep {
313 entry.keep_format();
314 }
315 entry.autoformat();
316 }
317 if let Some(children) = self.children.as_mut() {
318 children.autoformat_config(&FormatConfig {
319 indent_level: config.indent_level + 1,
320 ..*config
321 });
322 if let Some(KdlDocumentFormat { leading, trailing }) = children.format_mut() {
323 *leading = leading.trim().into();
324 leading.push('\n');
325 for _ in 0..config.indent_level {
326 trailing.push_str(config.indent);
327 }
328 }
329 }
330 }
331
332 pub fn parse(s: &str) -> Result<Self, KdlError> {
339 #[cfg(not(feature = "v1-fallback"))]
340 {
341 v2_parser::try_parse(v2_parser::padded_node, s)
342 }
343 #[cfg(feature = "v1-fallback")]
344 {
345 v2_parser::try_parse(v2_parser::padded_node, s)
346 .or_else(|e| KdlNode::parse_v1(s).map_err(|_| e))
347 }
348 }
349
350 #[cfg(feature = "v1")]
352 pub fn parse_v1(s: &str) -> Result<Self, KdlError> {
353 let ret: Result<kdlv1::KdlNode, kdlv1::KdlError> = s.parse();
354 ret.map(|x| x.into()).map_err(|e| e.into())
355 }
356
357 pub fn ensure_v2(&mut self) {
359 self.ty = self.ty.take().map(|ty| ty.value().into());
360 let v2_name: KdlIdentifier = self.name.value().into();
361 self.name = v2_name;
362 for entry in self.iter_mut() {
363 entry.ensure_v2();
364 }
365 self.children = self.children.take().map(|mut doc| {
366 doc.ensure_v2();
367 doc
368 });
369 }
370
371 #[cfg(feature = "v1")]
373 pub fn ensure_v1(&mut self) {
374 self.ty = self.ty.take().map(|ty| {
375 let v1_name: kdlv1::KdlIdentifier = ty.value().into();
376 v1_name.into()
377 });
378 let v1_name: kdlv1::KdlIdentifier = self.name.value().into();
379 self.name = v1_name.into();
380 for entry in self.iter_mut() {
381 entry.ensure_v1();
382 }
383 self.children = self.children.take().map(|mut children| {
384 children.ensure_v1();
385 children
386 });
387 }
388}
389
390#[cfg(feature = "v1")]
391impl From<kdlv1::KdlNode> for KdlNode {
392 fn from(value: kdlv1::KdlNode) -> Self {
393 let terminator = value
394 .trailing()
395 .map(|t| if t.contains(';') { ";" } else { "\n" })
396 .unwrap_or("\n");
397 let trailing = value.trailing().map(|t| {
398 if t.contains(';') {
399 t.replace(';', "")
400 } else {
401 let t = t.replace("\r\n", "\n");
402 let t = t
403 .chars()
404 .map(|c| {
405 if v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)) {
406 '\n'
407 } else {
408 c
409 }
410 })
411 .collect::<String>();
412 if terminator == ";" {
413 t
414 } else {
415 t.replacen('\n', "", 1)
416 }
417 }
418 });
419 KdlNode {
420 ty: value.ty().map(|x| x.clone().into()),
421 name: value.name().clone().into(),
422 entries: value.entries().iter().map(|x| x.clone().into()).collect(),
423 children: value.children().map(|x| x.clone().into()),
424 format: Some(KdlNodeFormat {
425 leading: value.leading().unwrap_or("").into(),
426 before_ty_name: "".into(),
427 after_ty_name: "".into(),
428 after_ty: "".into(),
429 before_children: value.before_children().unwrap_or("").into(),
430 before_terminator: "".into(),
431 terminator: terminator.into(),
432 trailing: trailing.unwrap_or_else(|| "".into()),
433 }),
434 #[cfg(feature = "span")]
435 span: SourceSpan::new(value.span().offset().into(), value.span().len()),
436 }
437 }
438}
439
440#[derive(Debug)]
484pub struct EntryIter<'a>(Iter<'a, KdlEntry>);
485impl<'a> Iterator for EntryIter<'a> {
486 type Item = &'a KdlEntry;
487
488 fn next(&mut self) -> Option<Self::Item> {
489 self.0.next()
490 }
491}
492
493#[derive(Debug)]
495pub struct EntryIterMut<'a>(IterMut<'a, KdlEntry>);
496impl<'a> Iterator for EntryIterMut<'a> {
497 type Item = &'a mut KdlEntry;
498
499 fn next(&mut self) -> Option<Self::Item> {
500 self.0.next()
501 }
502}
503
504#[derive(Debug)]
506pub struct ChildrenIter<'a>(Option<Iter<'a, KdlNode>>);
507impl<'a> Iterator for ChildrenIter<'a> {
508 type Item = &'a KdlNode;
509
510 fn next(&mut self) -> Option<Self::Item> {
511 self.0.as_mut().and_then(|x| x.next())
512 }
513}
514
515#[derive(Debug)]
517pub struct ChildrenIterMut<'a>(Option<IterMut<'a, KdlNode>>);
518impl<'a> Iterator for ChildrenIterMut<'a> {
519 type Item = &'a mut KdlNode;
520
521 fn next(&mut self) -> Option<Self::Item> {
522 self.0.as_mut().and_then(|x| x.next())
523 }
524}
525
526impl KdlNode {
528 pub fn iter(&self) -> EntryIter<'_> {
530 EntryIter(self.entries.iter())
531 }
532
533 pub fn iter_mut(&mut self) -> EntryIterMut<'_> {
535 EntryIterMut(self.entries.iter_mut())
536 }
537
538 pub fn iter_children(&self) -> ChildrenIter<'_> {
541 ChildrenIter(self.children.as_ref().map(|x| x.nodes.iter()))
542 }
543
544 pub fn iter_children_mut(&mut self) -> ChildrenIterMut<'_> {
547 ChildrenIterMut(self.children.as_mut().map(|x| x.nodes.iter_mut()))
548 }
549
550 pub fn get(&self, key: impl Into<NodeKey>) -> Option<&KdlValue> {
553 self.entry_impl(key.into()).map(|e| &e.value)
554 }
555
556 pub fn get_mut(&mut self, key: impl Into<NodeKey>) -> Option<&mut KdlValue> {
559 self.entry_mut_impl(key.into()).map(|e| &mut e.value)
560 }
561
562 pub fn len(&self) -> usize {
564 self.entries.len()
565 }
566
567 pub fn is_empty(&self) -> bool {
569 self.entries.is_empty()
570 }
571
572 pub fn clear(&mut self) {
574 self.entries.clear();
575 }
576
577 pub fn push(&mut self, entry: impl Into<KdlEntry>) {
579 self.entries.push(entry.into());
580 }
581
582 pub fn insert(
589 &mut self,
590 key: impl Into<NodeKey>,
591 entry: impl Into<KdlEntry>,
592 ) -> Option<KdlEntry> {
593 self.insert_impl(key.into(), entry.into())
594 }
595
596 fn insert_impl(&mut self, key: NodeKey, mut entry: KdlEntry) -> Option<KdlEntry> {
597 match key {
598 NodeKey::Key(ref key_val) => {
599 if entry.name.is_none() {
600 entry.name = Some(key_val.clone());
601 }
602 if entry.name.as_ref().map(|i| i.value()) != Some(key_val.value()) {
603 panic!("Property name mismatch");
604 }
605 if let Some(existing) = self.entry_mut(key) {
606 std::mem::swap(existing, &mut entry);
607 Some(entry)
608 } else {
609 self.entries.push(entry);
610 None
611 }
612 }
613 NodeKey::Index(idx) => {
614 if entry.name.is_some() {
615 panic!("Cannot insert property with name under a numerical key");
616 }
617 let mut current_idx = 0;
618 for (idx_existing, existing) in self.entries.iter().enumerate() {
619 if existing.name.is_none() {
620 if current_idx == idx {
621 self.entries.insert(idx_existing, entry);
622 return None;
623 }
624 current_idx += 1;
625 }
626 }
627 if idx > current_idx {
628 panic!("Insertion index (is {idx}) should be <= len (is {current_idx})");
629 } else {
630 self.entries.push(entry);
631 None
632 }
633 }
634 }
635 }
636
637 pub fn remove(&mut self, key: impl Into<NodeKey>) -> Option<KdlEntry> {
696 self.remove_impl(key.into())
697 }
698
699 fn remove_impl(&mut self, key: NodeKey) -> Option<KdlEntry> {
700 match key {
701 NodeKey::Key(key) => {
702 for (idx, entry) in self.entries.iter().enumerate() {
703 if entry.name.is_some() && entry.name.as_ref() == Some(&key) {
704 return Some(self.entries.remove(idx));
705 }
706 }
707 None
708 }
709 NodeKey::Index(idx) => {
710 let mut current_idx = 0;
711 for (idx_entry, entry) in self.entries.iter().enumerate() {
712 if entry.name.is_none() {
713 if current_idx == idx {
714 return Some(self.entries.remove(idx_entry));
715 }
716 current_idx += 1;
717 }
718 }
719 panic!(
720 "removal index (is {idx}) should be < number of index entries (is {current_idx})"
721 );
722 }
723 }
724 }
725
726 pub fn retain<F>(&mut self, keep: F)
728 where
729 F: FnMut(&KdlEntry) -> bool,
730 {
731 self.entries.retain(keep)
732 }
733
734 pub fn sort_by<F>(&mut self, compare: F)
737 where
738 F: FnMut(&KdlEntry, &KdlEntry) -> Ordering,
739 {
740 self.entries.sort_by(compare)
741 }
742
743 pub fn sort_by_key<K, F>(&mut self, f: F)
746 where
747 F: FnMut(&KdlEntry) -> K,
748 K: Ord,
749 {
750 self.entries.sort_by_key(f)
751 }
752}
753
754#[derive(Debug, Clone, PartialEq, Eq)]
756pub enum NodeKey {
757 Key(KdlIdentifier),
759 Index(usize),
761}
762
763impl From<&str> for NodeKey {
764 fn from(key: &str) -> Self {
765 Self::Key(key.into())
766 }
767}
768
769impl From<String> for NodeKey {
770 fn from(key: String) -> Self {
771 Self::Key(key.into())
772 }
773}
774
775impl From<usize> for NodeKey {
776 fn from(key: usize) -> Self {
777 Self::Index(key)
778 }
779}
780
781impl Index<usize> for KdlNode {
782 type Output = KdlValue;
783
784 fn index(&self, index: usize) -> &Self::Output {
785 self.get(index).expect("Argument out of range.")
786 }
787}
788
789impl IndexMut<usize> for KdlNode {
790 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
791 self.get_mut(index).expect("Argument out of range.")
792 }
793}
794
795impl Index<&str> for KdlNode {
796 type Output = KdlValue;
797
798 fn index(&self, key: &str) -> &Self::Output {
799 self.get(key).expect("No such property.")
800 }
801}
802
803impl IndexMut<&str> for KdlNode {
804 fn index_mut(&mut self, key: &str) -> &mut Self::Output {
805 if self.get(key).is_none() {
806 self.push((key, KdlValue::Null));
807 }
808 self.get_mut(key).expect("Something went wrong.")
809 }
810}
811
812impl FromStr for KdlNode {
813 type Err = KdlError;
814
815 fn from_str(input: &str) -> Result<Self, Self::Err> {
816 v2_parser::try_parse(v2_parser::padded_node, input)
817 }
818}
819
820impl Display for KdlNode {
821 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
822 self.stringify(f, 0)
823 }
824}
825
826impl KdlNode {
827 pub(crate) fn stringify(
828 &self,
829 f: &mut std::fmt::Formatter<'_>,
830 indent: usize,
831 ) -> std::fmt::Result {
832 if let Some(KdlNodeFormat { leading, .. }) = self.format() {
833 write!(f, "{leading}")?;
834 } else {
835 write!(f, "{:indent$}", "", indent = indent)?;
836 }
837 if let Some(ty) = &self.ty {
838 write!(f, "({ty})")?;
839 }
840 write!(f, "{}", self.name)?;
841 let mut space_before_children = true;
842 for entry in &self.entries {
843 if entry.format().is_none() {
844 write!(f, " ")?;
845 }
846 write!(f, "{entry}")?;
847 space_before_children = entry.format().is_none();
848 }
849 if let Some(children) = &self.children {
850 if let Some(KdlNodeFormat {
851 before_children, ..
852 }) = self.format()
853 {
854 write!(f, "{before_children}")?;
855 } else if space_before_children {
856 write!(f, " ")?;
857 }
858 write!(f, "{{")?;
859 if children.format().is_none() {
860 writeln!(f)?;
861 }
862 children.stringify(f, indent + 4)?;
863 if children.format().is_none() {
864 write!(f, "{:indent$}", "", indent = indent)?;
865 }
866 write!(f, "}}")?;
867 }
868 if let Some(KdlNodeFormat {
869 before_terminator,
870 terminator,
871 trailing,
872 ..
873 }) = self.format()
874 {
875 write!(f, "{before_terminator}{terminator}{trailing}")?;
876 }
877 Ok(())
878 }
879}
880
881#[derive(Debug, Clone, Default, Hash, Eq, PartialEq)]
883pub struct KdlNodeFormat {
884 pub leading: String,
886 pub before_ty_name: String,
888 pub after_ty_name: String,
890 pub after_ty: String,
892 pub before_children: String,
894 pub before_terminator: String,
896 pub terminator: String,
898 pub trailing: String,
900}
901
902#[cfg(test)]
903mod test {
904 use super::*;
905
906 #[test]
907 fn canonical_clear_fmt() -> miette::Result<()> {
908 let mut left_node: KdlNode = r#"node /-"commented" param_name=103.000 {
909 // This is a nested node
910 nested 1 2 3
911 }"#
912 .parse()?;
913 let mut right_node: KdlNode = "node param_name=103.0 { nested 1 2 3; }".parse()?;
914 assert_ne!(left_node, right_node);
915 left_node.clear_format_recursive();
916 right_node.clear_format_recursive();
917 assert_eq!(left_node.to_string(), right_node.to_string());
918 Ok(())
919 }
920
921 #[test]
922 fn parsing() -> miette::Result<()> {
923 let node: KdlNode = "\n\t (\"ty\")\"node\" 0xDEADbeef;\n".parse()?;
924 assert_eq!(node.ty(), Some(&"\"ty\"".parse()?));
925 assert_eq!(node.name(), &"\"node\"".parse()?);
926 assert_eq!(node.entry(0), Some(&" 0xDEADbeef".parse()?));
927 assert_eq!(
928 node.format(),
929 Some(&KdlNodeFormat {
930 leading: "\n\t ".into(),
931 before_terminator: "".into(),
932 terminator: ";".into(),
933 trailing: "\n".into(),
934 before_ty_name: "".into(),
935 after_ty_name: "".into(),
936 after_ty: "".into(),
937 before_children: "".into(),
938 })
939 );
940
941 let node: KdlNode = r#"node test {
942 link "blah" anything=self
943}
944"#
945 .parse::<KdlNode>()?;
946 assert_eq!(node.entry(0), Some(&" test".parse()?));
947 assert_eq!(node.children().unwrap().nodes().len(), 1);
948
949 Ok(())
950 }
951
952 #[test]
953 fn indexing() {
954 let mut node = KdlNode::new("foo");
955 node.push("bar");
956 node["foo"] = 1.into();
957
958 assert_eq!(node[0], "bar".into());
959 assert_eq!(node["foo"], 1.into());
960
961 node[0] = false.into();
962 node["foo"] = KdlValue::Null;
963
964 assert_eq!(node[0], false.into());
965 assert_eq!(node["foo"], KdlValue::Null);
966
967 node.entries_mut().push(KdlEntry::new_prop("x", 1));
968 node.entries_mut().push(KdlEntry::new_prop("x", 2));
969 assert_eq!(&node["x"], &2.into())
970 }
971
972 #[test]
973 fn insertion() {
974 let mut node = KdlNode::new("foo");
975 node.push("pos0");
976 node.insert("keyword", 6.0);
977 node.push("pos1");
978 assert_eq!(node.entries().len(), 3);
979
980 node.insert(0, "inserted0");
981 node.insert(2, "inserted1");
982 assert_eq!(node.entries().len(), 5);
983 assert_eq!(node[0], "inserted0".into());
984 assert_eq!(node[1], "pos0".into());
985 assert_eq!(node[2], "inserted1".into());
986 assert_eq!(node[3], "pos1".into());
987 }
988
989 #[test]
990 fn removal() {
991 let mut node = KdlNode::new("foo");
992 node.push("pos0");
993 node.insert("keyword", 6.0);
994 node.push("pos1");
995 assert_eq!(node.entries().len(), 3);
996
997 node.remove(1);
998 assert_eq!(node.entries().len(), 2, "index removal should succeed");
999 assert!(
1000 node.get("keyword").is_some(),
1001 "keyword property should not be removed by index removal"
1002 );
1003 node.remove("not an existing keyword");
1004 assert_eq!(node.entries().len(), 2, "key removal should not succeed");
1005 node.remove("keyword");
1006 assert_eq!(node.entries().len(), 1, "key removal should succeed");
1007 node.remove(0);
1008 assert_eq!(node.entries().len(), 0, "index removal should suceed");
1009 }
1010
1011 #[test]
1012 #[should_panic(expected = "removal index (is 0) should be < number of index entries (is 0)")]
1013 fn remove_panic() {
1014 let mut node = KdlNode::new("foo");
1015 node.push("pos0");
1016 node.insert("keyword", 6.0);
1017 node.remove(0);
1018 assert_eq!(node.entries().len(), 1, "key removal should succeed");
1019 node.remove(0); }
1021}