kdl/
node.rs

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/// Represents an individual KDL
18/// [`Node`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#node) inside a
19/// KDL Document.
20#[derive(Debug, Clone, Eq)]
21pub struct KdlNode {
22    pub(crate) ty: Option<KdlIdentifier>,
23    pub(crate) name: KdlIdentifier,
24    // TODO: consider using `hashlink` for this instead, later.
25    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        // intentionally omitted: self.span == other.span
40    }
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        // Intentionally omitted: self.span.hash(state);
51    }
52}
53
54impl KdlNode {
55    /// Creates a new KdlNode with a given name.
56    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    /// Gets this node's name.
72    pub fn name(&self) -> &KdlIdentifier {
73        &self.name
74    }
75
76    /// Gets a mutable reference to this node's name.
77    pub fn name_mut(&mut self) -> &mut KdlIdentifier {
78        &mut self.name
79    }
80
81    /// Sets this node's name.
82    pub fn set_name(&mut self, name: impl Into<KdlIdentifier>) {
83        self.name = name.into();
84    }
85
86    /// Gets this node's span.
87    ///
88    /// This value will be properly initialized when created via [`KdlDocument::parse`]
89    /// but may become invalidated if the document is mutated. We do not currently
90    /// guarantee this to yield any particularly consistent results at that point.
91    #[cfg(feature = "span")]
92    pub fn span(&self) -> SourceSpan {
93        self.span
94    }
95
96    /// Sets this node's span.
97    #[cfg(feature = "span")]
98    pub fn set_span(&mut self, span: impl Into<SourceSpan>) {
99        self.span = span.into();
100    }
101
102    /// Gets the node's type identifier, if any.
103    pub fn ty(&self) -> Option<&KdlIdentifier> {
104        self.ty.as_ref()
105    }
106
107    /// Gets a mutable reference to the node's type identifier.
108    pub fn ty_mut(&mut self) -> &mut Option<KdlIdentifier> {
109        &mut self.ty
110    }
111
112    /// Sets the node's type identifier.
113    pub fn set_ty(&mut self, ty: impl Into<KdlIdentifier>) {
114        self.ty = Some(ty.into());
115    }
116
117    /// Returns a reference to this node's entries (arguments and properties).
118    pub fn entries(&self) -> &[KdlEntry] {
119        &self.entries
120    }
121
122    /// Returns a mutable reference to this node's entries (arguments and
123    /// properties).
124    pub fn entries_mut(&mut self) -> &mut Vec<KdlEntry> {
125        &mut self.entries
126    }
127
128    /// Clears leading and trailing text (whitespace, comments), as well as
129    /// the space before the children block, if any. Individual entries and
130    /// their formatting will be preserved.
131    ///
132    /// If you want to clear formatting on all children and entries as well,
133    /// use [`Self::clear_format_recursive`].
134    pub fn clear_format(&mut self) {
135        self.format = None;
136    }
137
138    /// Clears leading and trailing text (whitespace, comments), as well as
139    /// the space before the children block, if any. Individual entries and
140    /// children formatting will also be cleared.
141    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    /// Fetches an entry by key. Number keys will look up arguments, strings
153    /// will look up properties.
154    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    /// Fetches a mutable referene to an entry by key. Number keys will look
187    /// up arguments, strings will look up properties.
188    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    /// Returns a reference to this node's children, if any.
221    pub fn children(&self) -> Option<&KdlDocument> {
222        self.children.as_ref()
223    }
224
225    /// Returns a mutable reference to this node's children, if any.
226    pub fn children_mut(&mut self) -> &mut Option<KdlDocument> {
227        &mut self.children
228    }
229
230    /// Sets the KdlDocument representing this node's children.
231    pub fn set_children(&mut self, children: KdlDocument) {
232        self.children = Some(children);
233    }
234
235    /// Removes this node's children completely.
236    pub fn clear_children(&mut self) {
237        self.children = None;
238    }
239
240    /// Returns a mutable reference to this node's children [`KdlDocument`],
241    /// creating one first if one does not already exist.
242    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    /// Gets the formatting details (including whitespace and comments) for this node.
250    pub fn format(&self) -> Option<&KdlNodeFormat> {
251        self.format.as_ref()
252    }
253
254    /// Gets a mutable reference to this node's formatting details.
255    pub fn format_mut(&mut self) -> Option<&mut KdlNodeFormat> {
256        self.format.as_mut()
257    }
258
259    /// Sets the formatting details for this node.
260    pub fn set_format(&mut self, format: KdlNodeFormat) {
261        self.format = Some(format);
262    }
263    /// Auto-formats this node and its contents.
264    pub fn autoformat(&mut self) {
265        self.autoformat_config(&FormatConfig::default());
266    }
267
268    /// Auto-formats this node and its contents, stripping comments.
269    pub fn autoformat_no_comments(&mut self) {
270        self.autoformat_config(&FormatConfig {
271            no_comments: true,
272            ..Default::default()
273        });
274    }
275
276    /// Auto-formats this node and its contents according to `config`.
277    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    /// Parses a string into a node.
333    ///
334    /// If the `v1-fallback` feature is enabled, this method will first try to
335    /// parse the string as a KDL v2 node, and, if that fails, it will try
336    /// to parse again as a KDL v1 node. If both fail, only the v2 parse
337    /// errors will be returned.
338    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    /// Parses a KDL v1 string into a document.
351    #[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    /// Makes sure this node is in v2 format.
358    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    /// Makes sure this node is in v1 format.
372    #[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// Query language
441// impl KdlNode {
442// /// Queries this Node according to the KQL
443// query language, /// returning an iterator over all matching nodes. pub
444// fn query_all( &self, query: impl IntoKdlQuery, ) ->
445//     Result<KdlQueryIterator<'_>, KdlDiagnostic> { let q =
446//     query.into_query()?; Ok(KdlQueryIterator::new(Some(self), None, q))
447// }
448
449// /// Queries this Node according to the KQL query language,
450// /// returning the first match, if any.
451// pub fn query(&self, query: impl IntoKdlQuery) -> Result<Option<&KdlNode>, KdlDiagnostic> {
452//     Ok(self.query_all(query)?.next())
453// }
454
455// /// Queries this Node according to the KQL query language,
456// /// picking the first match, and calling `.get(key)` on it, if the query
457// /// succeeded.
458// pub fn query_get(
459//     &self,
460//     query: impl IntoKdlQuery,
461//     key: impl Into<NodeKey>,
462// ) -> Result<Option<&KdlValue>, KdlDiagnostic> {
463//     Ok(self.query(query)?.and_then(|node| node.get(key)))
464// }
465
466// /// Queries this Node according to the KQL query language,
467// /// returning an iterator over all matching nodes, returning the requested
468// /// field from each of those nodes and filtering out nodes that don't have
469// /// it.
470// pub fn query_get_all(
471//     &self,
472//     query: impl IntoKdlQuery,
473//     key: impl Into<NodeKey>,
474// ) -> Result<impl Iterator<Item = &KdlValue>, KdlDiagnostic> {
475//     let key: NodeKey = key.into();
476//     Ok(self
477//         .query_all(query)?
478//         .filter_map(move |node| node.get(key.clone())))
479// }
480//}
481
482/// Iterator for entries in a node, including properties.
483#[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/// Mutable iterator for entries in a node, including properties.
494#[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/// Iterator for child nodes for this node. Empty if there is no children block.
505#[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/// Iterator for child nodes for this node. Empty if there is no children block.
516#[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
526// Vec-style APIs
527impl KdlNode {
528    /// Returns an iterator over the node's entries, including properties.
529    pub fn iter(&self) -> EntryIter<'_> {
530        EntryIter(self.entries.iter())
531    }
532
533    /// Returns a mutable iterator over the node's entries, including properties.
534    pub fn iter_mut(&mut self) -> EntryIterMut<'_> {
535        EntryIterMut(self.entries.iter_mut())
536    }
537
538    /// Returns an iterator over the node's children, if any. Nodes without
539    /// children will return an empty iterator.
540    pub fn iter_children(&self) -> ChildrenIter<'_> {
541        ChildrenIter(self.children.as_ref().map(|x| x.nodes.iter()))
542    }
543
544    /// Returns a mutable iterator over the node's children, if any. Nodes
545    /// without children will return an empty iterator.
546    pub fn iter_children_mut(&mut self) -> ChildrenIterMut<'_> {
547        ChildrenIterMut(self.children.as_mut().map(|x| x.nodes.iter_mut()))
548    }
549
550    /// Gets a value by key. Number keys will look up arguments, strings will
551    /// look up properties.
552    pub fn get(&self, key: impl Into<NodeKey>) -> Option<&KdlValue> {
553        self.entry_impl(key.into()).map(|e| &e.value)
554    }
555
556    /// Fetches a mutable referene to an value by key. Number keys will look
557    /// up arguments, strings will look up properties.
558    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    /// Number of entries in this node.
563    pub fn len(&self) -> usize {
564        self.entries.len()
565    }
566
567    /// Returns true if this node is completely empty (including whitespace).
568    pub fn is_empty(&self) -> bool {
569        self.entries.is_empty()
570    }
571
572    /// Shorthand for `self.entries_mut().clear()`
573    pub fn clear(&mut self) {
574        self.entries.clear();
575    }
576
577    /// Shorthand for `self.entries_mut().push(entry)`.
578    pub fn push(&mut self, entry: impl Into<KdlEntry>) {
579        self.entries.push(entry.into());
580    }
581
582    /// Inserts an entry into this node. If an entry already exists with the
583    /// same string key, it will be replaced and the previous entry will be
584    /// returned.
585    ///
586    /// Numerical keys will insert arguments, string keys will insert
587    /// properties.
588    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 replace(
638    //     &mut self,
639    //     key: impl Into<NodeKey>,
640    //     entry: impl Into<KdlEntry>,
641    // ) -> Option<KdlEntry> {
642    //     self.replace_impl(key.into(), entry.into())
643    // }
644
645    // fn replace_impl(&mut self, key: NodeKey, mut entry: KdlEntry) -> Option<KdlEntry> {
646    //     todo!();
647    //     // match key {
648    //     //     NodeKey::Key(ref key_val) => {
649    //     //         if entry.name.is_none() {
650    //     //             entry.name = Some(key_val.clone());
651    //     //         }
652    //     //         if entry.name.as_ref().map(|i| i.value()) != Some(key_val.value()) {
653    //     //             panic!("Property name mismatch");
654    //     //         }
655    //     //         if let Some(existing) = self.entry_mut(key) {
656    //     //             std::mem::swap(existing, &mut entry);
657    //     //             Some(entry)
658    //     //         } else {
659    //     //             self.entries.push(entry);
660    //     //             None
661    //     //         }
662    //     //     }
663    //     //     NodeKey::Index(idx) => {
664    //     //         if entry.name.is_some() {
665    //     //             panic!("Cannot insert property with name under a numerical key");
666    //     //         }
667    //     //         let mut current_idx = 0;
668    //     //         for (idx_existing, existing) in self.entries.iter().enumerate() {
669    //     //             if existing.name.is_none() {
670    //     //                 if current_idx == idx {
671    //     //                     self.entries.replace(idx_existing, entry);
672    //     //                     return None;
673    //     //                 }
674    //     //                 current_idx += 1;
675    //     //             }
676    //     //         }
677    //     //         if idx > current_idx {
678    //     //             panic!(
679    //     //                 "Insertion index (is {}) should be <= len (is {})",
680    //     //                 idx, current_idx
681    //     //             );
682    //     //         } else {
683    //     //             self.entries.push(entry);
684    //     //             None
685    //     //         }
686    //     //     }
687    //     // }
688    // }
689
690    /// Removes an entry from this node. If an entry already exists with the
691    /// same key, it will be returned.
692    ///
693    /// Numerical keys will remove arguments, string keys will remove
694    /// properties.
695    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    /// Retains only the elements specified by the predicate.
727    pub fn retain<F>(&mut self, keep: F)
728    where
729        F: FnMut(&KdlEntry) -> bool,
730    {
731        self.entries.retain(keep)
732    }
733
734    /// Sorts the slice with a comparison function, preserving initial order of
735    /// equal elements.
736    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    /// Sorts the slice with a key extraction function, preserving initial order
744    /// of equal elements.
745    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/// Represents a [`KdlNode`]'s entry key.
755#[derive(Debug, Clone, PartialEq, Eq)]
756pub enum NodeKey {
757    /// Key for a node property entry.
758    Key(KdlIdentifier),
759    /// Index for a node argument entry (positional value).
760    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/// Formatting details for [`KdlNode`].
882#[derive(Debug, Clone, Default, Hash, Eq, PartialEq)]
883pub struct KdlNodeFormat {
884    /// Whitespace and comments preceding the node itself.
885    pub leading: String,
886    /// Whitespace and comments between the opening `(` of a type annotation and the actual annotation name.
887    pub before_ty_name: String,
888    /// Whitespace and comments between the annotation name and the closing `)`.
889    pub after_ty_name: String,
890    /// Whitespace and comments after a node's type annotation.
891    pub after_ty: String,
892    /// Whitespace and comments preceding the node's children block.
893    pub before_children: String,
894    /// Whitespace and comments right before the node's terminator.
895    pub before_terminator: String,
896    /// The terminator for the node.
897    pub terminator: String,
898    /// Whitespace and comments following the node itself, after the terminator.
899    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); // should panic here
1020    }
1021}