Skip to main content

kaydle_primitives/
node.rs

1/*!
2A system for parsing entire nodes and node lists. Unlike the rest of
3`kaydle-primitives`, this module doesn't expose ordinary nom parsers and
4container types. Instead, because it's designed specifically to support the
5implementation of a serde deserializer, it exposes a set of "processors", which
6can be used to fetch nodes or the contents of nodes step-by-step.
7
8This module makes extensive use of the builder traits defined in
9`kaydle-primitives` (such as [`ValueBuilder`] and [`StringBuilder`]) to allow
10callers to precisely control how much information they need from the node.
11Often you can use `()` instead of a real KDL type if you don't care about a
12value; this will be faster to parse.
13
14This module tries to be as misuse resistant as possible, using borrowing and
15move semantics to ensure that methods aren't called out of order. Where
16build-time correctness is impossible, it instead uses runtime tracking and
17panics to ensure consistent state.
18*/
19
20use std::char::CharTryFromError;
21
22use nom::{
23    branch::alt,
24    character::complete::char,
25    combinator::eof,
26    error::{FromExternalError, ParseError},
27    Err as NomErr, IResult, Parser,
28};
29use nom_supreme::{context::ContextError, tag::TagError, ParserExt};
30
31use crate::{
32    annotation::{with_annotation, AnnotationBuilder, GenericAnnotated, RecognizedAnnotation},
33    number::BoundsError,
34    property::{parse_property, GenericProperty},
35    string::{parse_identifier, StringBuilder},
36    value::{parse_value, ValueBuilder},
37    whitespace::{parse_linespace, parse_node_space, parse_node_terminator},
38};
39
40/// Run a parser on a mutable reference to some input. If the parse is
41/// successful, the input is updated in-place, and the result of the parse
42/// is returned.
43fn run_parser_on<I, O, E>(input: &mut I, mut parser: impl Parser<I, O, E>) -> Result<O, NomErr<E>>
44where
45    I: Clone,
46{
47    parser.parse(input.clone()).map(|(tail, value)| {
48        *input = tail;
49        value
50    })
51}
52
53/// Parse the annotation & identifier at the start of a node, or some other
54/// subparser indicating the end of a node list (either a } or an eof)
55fn parse_node_start<'i, T, A, E>(
56    end_of_nodes: impl Parser<&'i str, (), E>,
57) -> impl Parser<&'i str, Option<GenericAnnotated<A, T>>, E>
58where
59    T: StringBuilder<'i>,
60    A: AnnotationBuilder<'i>,
61    E: ParseError<&'i str>,
62    E: TagError<&'i str, &'static str>,
63    E: FromExternalError<&'i str, CharTryFromError>,
64    E: ContextError<&'i str, &'static str>,
65{
66    with_annotation(parse_identifier)
67        .map(Some)
68        .context("node")
69        .or(end_of_nodes.map(|()| None))
70        .preceded_by(parse_linespace)
71}
72
73/// A single node. Contains the name of the node as well as a [`NodeContent`],
74/// which is used to extract the arguments, properties, and children from
75/// the node.
76#[derive(Debug)]
77pub struct Node<'i, 'a, Name> {
78    /// The name of this node
79    pub name: Name,
80
81    /// A processor, used for getting the contents of the node.
82    pub content: NodeContent<'i, 'a>,
83}
84
85/// The outcome of a drain operation, indicating if the thing being drained
86/// was already empty.
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum DrainOutcome {
89    /// The thing was already empty
90    Empty,
91
92    /// There was unconsumed content in the thing
93    NotEmpty,
94}
95
96/// A recognized node. Used in the case where the caller cares *that* a node
97/// was successfully parsed, but not what the actual value of the node is.
98///
99/// Note that nodes are parsed lazily, so this really only recognizes the node
100/// name and annotation.
101pub type RecognizedNode<'i, 'a> = Node<'i, 'a, ()>;
102
103/// Trait for types that contain a node list. Abstracts over a [`Document`],
104/// which operates at the top level, and [`Children`] which are nested in `{ }`.
105pub trait NodeList<'i>: Sized {
106    /// Get the next node. Returns the [`Node`], if any, which includes the
107    /// name of the node as well as its [content][NodeContent].
108    ///
109    /// Note for implementors: this method should be fused, to ensure that
110    /// `drain` is always safe to call. After it returns Ok(None), it should
111    /// continue to return Ok(None) forever.
112    fn next_node<'s, Annotation, Name, E>(
113        &'s mut self,
114    ) -> Result<Option<GenericAnnotated<Annotation, Node<'i, 's, Name>>>, NomErr<E>>
115    where
116        Annotation: AnnotationBuilder<'i>,
117        Name: StringBuilder<'i>,
118        E: ParseError<&'i str>,
119        E: TagError<&'i str, &'static str>,
120        E: FromExternalError<&'i str, CharTryFromError>,
121        E: ContextError<&'i str, &'static str>;
122
123    /// Drain all remaining content from this nodelist. The nodelist is parsed,
124    /// and errors are returned, but the nodes are otherwise discarded.
125    ///
126    /// Returns [`DrainOutcome::NotEmpty`] if there is at least 1 node returned
127    /// by [`next_node`][Self::next_node].
128    fn drain<E>(mut self) -> Result<DrainOutcome, NomErr<E>>
129    where
130        E: ParseError<&'i str>,
131        E: TagError<&'i str, &'static str>,
132        E: FromExternalError<&'i str, CharTryFromError>,
133        E: FromExternalError<&'i str, BoundsError>,
134        E: ContextError<&'i str, &'static str>,
135    {
136        match self.next_node()? {
137            None => Ok(DrainOutcome::Empty),
138            Some(RecognizedAnnotation {
139                item: RecognizedNode { content, .. },
140                ..
141            }) => {
142                content.drain()?;
143
144                while let Some(RecognizedAnnotation {
145                    item: RecognizedNode { content, .. },
146                    ..
147                }) = self.next_node()?
148                {
149                    content.drain()?;
150                }
151
152                Ok(DrainOutcome::NotEmpty)
153            }
154        }
155    }
156}
157
158impl<'i, T: NodeList<'i>> NodeList<'i> for &mut T {
159    fn next_node<'s, Annotation, Name, E>(
160        &'s mut self,
161    ) -> Result<Option<GenericAnnotated<Annotation, Node<'i, 's, Name>>>, NomErr<E>>
162    where
163        Annotation: AnnotationBuilder<'i>,
164        Name: StringBuilder<'i>,
165        E: ParseError<&'i str>,
166        E: TagError<&'i str, &'static str>,
167        E: FromExternalError<&'i str, CharTryFromError>,
168        E: ContextError<&'i str, &'static str>,
169    {
170        T::next_node(*self)
171    }
172}
173
174/// Container for a top level kdl document. Returns the nodes in the document.
175#[derive(Debug, Clone)]
176pub struct Document<'i> {
177    /// The currently unparsed input string, as a suffix of the original input.
178    state: &'i str,
179
180    /// Bool that ensures that node processors fully consume their nodes, so
181    /// that parse state remains consistent. Set to true when a node processor
182    /// is returned, and only resets to false when that processor is finished.
183    child_in_progress: bool,
184}
185
186impl<'i> Document<'i> {
187    /// Create a new `Document` from an input string.
188    pub fn new(input: &'i str) -> Self {
189        Self {
190            state: input,
191            child_in_progress: false,
192        }
193    }
194
195    fn run_parser<T, E>(&mut self, parser: impl Parser<&'i str, T, E>) -> Result<T, NomErr<E>> {
196        run_parser_on(&mut self.state, parser)
197    }
198}
199
200impl<'i> NodeList<'i> for Document<'i> {
201    fn next_node<'s, Annotation, Name, E>(
202        &'s mut self,
203    ) -> Result<Option<GenericAnnotated<Annotation, Node<'i, 's, Name>>>, NomErr<E>>
204    where
205        Annotation: AnnotationBuilder<'i>,
206        Name: StringBuilder<'i>,
207        E: ParseError<&'i str>,
208        E: TagError<&'i str, &'static str>,
209        E: FromExternalError<&'i str, CharTryFromError>,
210        E: ContextError<&'i str, &'static str>,
211    {
212        if self.child_in_progress {
213            panic!(
214                "Called next_node before the previous node was fully parsed. This is a kaydle bug."
215            )
216        }
217
218        self.run_parser(parse_node_start(eof.value(())))
219            .map(move |opt_name| {
220                opt_name.map(move |annotated_name| {
221                    self.child_in_progress = true;
222
223                    annotated_name.map_item(|name| Node {
224                        name,
225                        content: NodeContent {
226                            state: &mut self.state,
227                            in_progress: &mut self.child_in_progress,
228                        },
229                    })
230                })
231            })
232    }
233}
234
235enum InternalNodeEvent<
236    ArgumentAnnotation,
237    Argument,
238    PropertyKey,
239    PropertyValueAnnotation,
240    PropertyValue,
241> {
242    Argument(GenericAnnotated<ArgumentAnnotation, Argument>),
243    Property(GenericProperty<PropertyKey, PropertyValueAnnotation, PropertyValue>),
244    Children,
245    End,
246}
247
248/// A piece of content from a node.
249#[derive(Debug)]
250pub enum NodeEvent<
251    'i,
252    'p,
253    ArgumentAnnotation,
254    Argument,
255    PropertyKey,
256    PropertyValueAnnotation,
257    PropertyValue,
258> {
259    /// An argument from a node
260    Argument {
261        /// The value, with its annotation
262        argument: GenericAnnotated<ArgumentAnnotation, Argument>,
263        /// The processor containing the rest of the node
264        tail: NodeContent<'i, 'p>,
265    },
266
267    /// A property (key-value pair) from a node
268    Property {
269        /// The property
270        property: GenericProperty<PropertyKey, PropertyValueAnnotation, PropertyValue>,
271        /// The processor containing the rest of the node
272        tail: NodeContent<'i, 'p>,
273    },
274
275    /// A set of children from the node.
276    Children {
277        /// A `NodeListProcessor` used to get child nodes one-by-one
278        children: Children<'i, 'p>,
279    },
280
281    /// There was nothing else in the node.
282    End,
283}
284
285/// A [`NodeEvent`] containing no data. Used when the caller care what _kind_ of
286/// thing was in the node, but not the actual value / content.
287pub type RecognizedNodeEvent<'i, 'p> = NodeEvent<'i, 'p, (), (), (), (), ()>;
288
289fn parse_node_event<'i, E, VA, V, K, PA, P>(
290    input: &'i str,
291) -> IResult<&'i str, InternalNodeEvent<VA, V, K, PA, P>, E>
292where
293    V: ValueBuilder<'i>,
294    VA: AnnotationBuilder<'i>,
295    K: StringBuilder<'i>,
296    P: ValueBuilder<'i>,
297    PA: AnnotationBuilder<'i>,
298    E: ParseError<&'i str>,
299    E: TagError<&'i str, &'static str>,
300    E: FromExternalError<&'i str, CharTryFromError>,
301    E: FromExternalError<&'i str, BoundsError>,
302    E: ContextError<&'i str, &'static str>,
303{
304    alt((
305        // Parse a value or property, preceded by 1 or more whitespace
306        alt((
307            // Important: make sure to try to parse a property first, since
308            // "abc"=10 could be conservatively parsed as just the value "abc"
309            parse_property
310                .map(InternalNodeEvent::Property)
311                .context("property"),
312            parse_value
313                .map(InternalNodeEvent::Argument)
314                .context("value"),
315        ))
316        .preceded_by(parse_node_space),
317        // Parse children or a node terminator, preceded by 0 or more whitespace
318        alt((
319            char('{')
320                .map(|_| InternalNodeEvent::Children)
321                .context("children"),
322            parse_node_terminator.map(|()| InternalNodeEvent::End),
323        ))
324        .preceded_by(parse_node_space.opt()),
325    ))
326    .parse(input)
327}
328
329/// Type for retrieving the content (arguments, properties, and children) of
330/// a single node. It's important to ensure you drain or otherwise consume all
331/// events from this processor, or else the parent parser will be left in an
332/// inconsistent state.
333#[derive(Debug)]
334pub struct NodeContent<'i, 'p> {
335    // The state of the original document; owned by a `Document`.
336    state: &'p mut &'i str,
337
338    /// Bool owned by the parent's list processor. Must be set to false only when
339    /// this node has been fully consumed.
340    in_progress: &'p mut bool,
341}
342
343impl<'i, 'p> NodeContent<'i, 'p> {
344    /// Get the next piece of content from a node. This can be an argument,
345    /// a property, a set of children, or [`End`][NodeEvent::End] if the node
346    /// is done.
347    ///
348    /// For correctness, this method is move oriented. If the event is an
349    /// argument or property, the event includes a new [`NodeContent`] for
350    /// fetching the rest of the node. Conversely, if the event is children or
351    /// the end of the node, the processor is consumed, because there's nothing
352    /// more that can be retrieved from this node.
353    pub fn next_event<
354        ArgumentAnnotation,
355        Argument,
356        PropertyKey,
357        PropertyValueAnnotation,
358        PropertyValue,
359        Error,
360    >(
361        mut self,
362    ) -> Result<
363        NodeEvent<
364            'i,
365            'p,
366            ArgumentAnnotation,
367            Argument,
368            PropertyKey,
369            PropertyValueAnnotation,
370            PropertyValue,
371        >,
372        NomErr<Error>,
373    >
374    where
375        Argument: ValueBuilder<'i>,
376        ArgumentAnnotation: AnnotationBuilder<'i>,
377        PropertyKey: StringBuilder<'i>,
378        PropertyValue: ValueBuilder<'i>,
379        PropertyValueAnnotation: AnnotationBuilder<'i>,
380        Error: ParseError<&'i str>,
381        Error: TagError<&'i str, &'static str>,
382        Error: FromExternalError<&'i str, CharTryFromError>,
383        Error: FromExternalError<&'i str, BoundsError>,
384        Error: ContextError<&'i str, &'static str>,
385    {
386        // Because we use a move-oriented interface, there's no need to check
387        // in_progress. We (or the children processor we return) just need to
388        // make sure it's reset to false when we're done.
389        self.run_parser(parse_node_event)
390            .map(move |event| match event {
391                InternalNodeEvent::Argument(argument) => NodeEvent::Argument {
392                    argument,
393                    tail: self,
394                },
395                InternalNodeEvent::Property(property) => NodeEvent::Property {
396                    property,
397                    tail: self,
398                },
399                InternalNodeEvent::Children => NodeEvent::Children {
400                    children: Children {
401                        state: self.state,
402                        in_progress: self.in_progress,
403                        child_in_progress: false,
404                    },
405                },
406                InternalNodeEvent::End => {
407                    *self.in_progress = false;
408                    NodeEvent::End
409                }
410            })
411    }
412
413    /// Parse and discard everything in this node. Checks for parse errors, but
414    /// otherwise discards all data. Returns [`DrainOutcome::Empty`] unless
415    /// there are any remaining properties, arguments, or non-empty children
416    /// in this node.
417    pub fn drain<E>(mut self) -> Result<DrainOutcome, NomErr<E>>
418    where
419        E: ParseError<&'i str>,
420        E: TagError<&'i str, &'static str>,
421        E: FromExternalError<&'i str, CharTryFromError>,
422        E: FromExternalError<&'i str, BoundsError>,
423        E: ContextError<&'i str, &'static str>,
424    {
425        self = match self.next_event()? {
426            RecognizedNodeEvent::Argument { tail, .. } => tail,
427            RecognizedNodeEvent::Property { tail, .. } => tail,
428            RecognizedNodeEvent::Children { children } => return children.drain(),
429            RecognizedNodeEvent::End => return Ok(DrainOutcome::Empty),
430        };
431
432        loop {
433            self = match self.next_event()? {
434                RecognizedNodeEvent::Argument { tail, .. } => tail,
435                RecognizedNodeEvent::Property { tail, .. } => tail,
436                RecognizedNodeEvent::Children { children } => {
437                    children.drain()?;
438                    return Ok(DrainOutcome::NotEmpty);
439                }
440                RecognizedNodeEvent::End => return Ok(DrainOutcome::NotEmpty),
441            }
442        }
443    }
444
445    fn run_parser<T, E>(&mut self, parser: impl Parser<&'i str, T, E>) -> Result<T, NomErr<E>> {
446        run_parser_on(self.state, parser)
447    }
448}
449
450/// Processor for child nodes of a particular node (contained in `{ }`).
451/// Returns the child nodes.
452#[derive(Debug)]
453pub struct Children<'i, 'p> {
454    state: &'p mut &'i str,
455
456    /// Bool owned by the parent's list processor. Must be set to true only when
457    /// this list has been fully consumed. Additionally used to ensure that
458    /// `next_node` exhibits fused behavior.
459    in_progress: &'p mut bool,
460
461    /// Bool that ensures that node processors fully consume their nodes, so
462    /// that parse state remains consistent. Set to false when a node processor
463    /// is returns, and only resets to true when that processor is finished.
464    child_in_progress: bool,
465}
466
467impl<'i> Children<'i, '_> {
468    fn run_parser<T, E>(&mut self, parser: impl Parser<&'i str, T, E>) -> Result<T, NomErr<E>> {
469        run_parser_on(self.state, parser)
470    }
471}
472
473impl<'i> NodeList<'i> for Children<'i, '_> {
474    fn next_node<'s, A, N, E>(
475        &'s mut self,
476    ) -> Result<Option<GenericAnnotated<A, Node<'i, 's, N>>>, nom::Err<E>>
477    where
478        N: StringBuilder<'i>,
479        A: AnnotationBuilder<'i>,
480        E: ParseError<&'i str>,
481        E: TagError<&'i str, &'static str>,
482
483        E: FromExternalError<&'i str, CharTryFromError>,
484        E: ContextError<&'i str, &'static str>,
485    {
486        // If the *parent* is at a node boundary, it means that *this*
487        // set of children was previously completed.
488        if !*self.in_progress {
489            return Ok(None);
490        }
491
492        // We *must* be at a node boundary internally; if we're not, it means one of our
493        // children wasn't fully consumed.
494        if self.child_in_progress {
495            panic!(
496                "Called next_node before the previous node was fully parsed. This is a kaydle bug."
497            )
498        }
499
500        self.run_parser(parse_node_start(char('}').value(())))
501            .map(|opt_name| match opt_name {
502                // None here means that we successfully parsed the end-of-children. Inform the parent.
503                None => {
504                    *self.in_progress = false;
505                    None
506                }
507                Some(annotated_name) => {
508                    self.child_in_progress = true;
509
510                    Some(annotated_name.map_item(|name| Node {
511                        name,
512                        content: NodeContent {
513                            state: self.state,
514                            in_progress: &mut self.child_in_progress,
515                        },
516                    }))
517                }
518            })
519    }
520}
521
522/// This test may not look like much, but all the relevant components are
523/// separately tested. If `.drain()` works, it's very likely the entire
524/// processor does too
525#[test]
526fn test_full_document_drain() {
527    let content = r##"
528    // This is a KDL document!
529    node1 "arg1" prop=10 {
530        (u8)item 10
531        (u8)item 20
532        items {
533            a /* An important note here */ "abc"
534            d "def"; g "ghi"
535        }
536    }
537    (annotated)node2
538    primitives null false true 10 10.5 -10 -10.5 3e6 0x10c 0b00001111 0o755
539    (a)annotated (n)null (f)false (t)true (i)10 (f)10.5 (n)-10.5e7
540    "##;
541
542    let processor = Document::new(content);
543
544    let res: Result<DrainOutcome, nom::Err<()>> = processor.drain();
545    assert_eq!(res.expect("parse error"), DrainOutcome::NotEmpty);
546}