yaml_ast/
lib.rs

1use crate::events::{Event, IntoEvents};
2
3pub mod emitter;
4pub mod events;
5
6pub enum Error {}
7
8/// A stream represents one or more [`Document`]s separated by `---`
9/// (triple dash) and `...` (triple dot).
10#[derive(Debug, Default)]
11pub struct Stream(Vec<Document>);
12
13impl Stream {
14    /// Creates a new (empty) stream of YAML [`Document`]s.
15    pub fn new() -> Self {
16        Self::default()
17    }
18
19    /// Appends one YAML [`Document`] at the end of the stream.
20    pub fn push_document(&mut self, document: Document) -> &mut Self {
21        self.0.push(document);
22        self
23    }
24}
25
26impl IntoEvents for Stream {
27    fn into_events(self) -> Vec<Event> {
28        let mut events = Vec::new();
29        events.push(Event::StreamStart);
30
31        for doc in self.0 {
32            events.extend(doc.into_events())
33        }
34
35        events.push(Event::StreamEnd);
36        events
37    }
38}
39
40/// A document is part (or chunk) of a larger [`Stream`].
41///
42/// Each document can have zero or more directives attached to it. These
43/// directives influence the behavior of the YAML processor. The content of the
44/// document is stored in zero or more [`Node`]s.
45#[derive(Debug, Default)]
46pub struct Document {
47    pub directives: Vec<String>,
48    pub nodes: Vec<Node>,
49}
50
51impl IntoEvents for Document {
52    fn into_events(self) -> Vec<Event> {
53        let mut events = Vec::new();
54        events.push(Event::DocumentStart);
55
56        for node in self.nodes {
57            events.extend(node.into_events())
58        }
59
60        events.push(Event::DocumentEnd);
61        events
62    }
63}
64
65impl Document {
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    /// Convenience function to create a new document from a mapping. Most YAML
71    /// documents have a mapping node as their root node.
72    pub fn from_mapping(mapping: Mapping) -> Self {
73        Self {
74            nodes: Vec::from([Node::Mapping(mapping)]),
75            ..Default::default()
76        }
77    }
78
79    pub fn push_directive(&mut self, directive: String) -> &mut Self {
80        self.directives.push(directive);
81        self
82    }
83
84    pub fn push_node(&mut self, node: Node) -> &mut Self {
85        self.nodes.push(node);
86        self
87    }
88}
89
90#[derive(Debug)]
91pub enum ScopedTag {
92    Global(Node),
93
94    // TODO (Techassi): Let's see how we can deal with custom tags
95    Local(Node),
96}
97
98impl Default for ScopedTag {
99    fn default() -> Self {
100        Self::Global(Node::default())
101    }
102}
103
104// TODO (Techassi): Ensure keys are unique in mappings
105/// Type alias for a [`Vec<(Node, Node)>`].
106pub type Mapping = Vec<(Node, Node)>;
107
108/// Type alias for a [`Vec<Node>`].
109pub type Sequence = Vec<Node>;
110
111/// A YAML schema is a combination of a set of tags and a mechanism for
112/// resolving non-specific tags.
113///
114/// The tags are specified in different schemas. The spec defined three main
115/// schemas: Failsafe, JSON, and Core. The tags are grouped in the following
116/// way:
117///
118/// - Failsafe Schema:
119///     - Mapping
120///     - Sequence
121///     - String
122/// - JSON Schema:
123///     - Null
124///     - Boolean
125///     - Integer
126///     - Floating Point
127///
128/// #### Note
129///
130/// The YAML specification defines nodes and tags a two separate (but related)
131/// concepts. Because Rust allows us to combine enums with structured data,
132/// this crate decides to combine both these concepts into one.
133#[derive(Debug)]
134pub enum Node {
135    /// Represents an associative container, where each key is unique in the
136    /// association and mapped to exactly one value.
137    ///
138    /// See <https://yaml.org/spec/1.2.2/#10111-generic-mapping>
139    Mapping(Vec<(Node, Node)>),
140
141    /// Represents a collection indexed by sequential integers starting with
142    /// zero.
143    ///
144    /// See <https://yaml.org/spec/1.2.2/#10112-generic-sequence>
145    Sequence(Vec<Node>),
146
147    /// Represents a Unicode string, a sequence of zero or more Unicode
148    /// characters.
149    ///
150    /// See <https://yaml.org/spec/1.2.2/#0113-generic-string>
151    String(String),
152
153    /// Represents the lack of a value.
154    ///
155    /// See <https://yaml.org/spec/1.2.2/#10211-null>
156    Null,
157
158    /// Represents a true/false value.
159    ///
160    /// See <https://yaml.org/spec/1.2.2/#10212-boolean>
161    Boolean(bool),
162
163    /// Represents arbitrary sized finite mathematical integers.
164    ///
165    /// See <https://yaml.org/spec/1.2.2/#10213-integer>
166    Integer(i64),
167
168    /// Represents an approximation to real numbers.
169    ///
170    /// See <https://yaml.org/spec/1.2.2/#10214-floating-point>
171    FloatingPoint(String),
172}
173
174impl Default for Node {
175    fn default() -> Self {
176        Self::Null
177    }
178}
179
180impl IntoEvents for Node {
181    fn into_events(self) -> Vec<Event> {
182        let mut events = Vec::new();
183
184        match self {
185            Node::Mapping(mapping) => {
186                events.push(Event::MappingStart(0));
187
188                for (k, v) in mapping {
189                    events.extend(k.into_events());
190                    events.extend(v.into_events());
191                }
192
193                events.push(Event::MappingEnd);
194            }
195            Node::Sequence(sequence) => {
196                events.push(Event::SequenceStart(0));
197
198                for item in sequence {
199                    events.extend(item.into_events());
200                }
201
202                events.push(Event::SequenceEnd);
203            }
204            Node::String(s) => events.push(Event::Scalar(s)),
205            Node::Null => events.push(Event::Scalar("null".into())),
206            Node::Boolean(b) => events.push(Event::Scalar(b.to_string())),
207            Node::Integer(i) => events.push(Event::Scalar(i.to_string())),
208            Node::FloatingPoint(_) => todo!(),
209        }
210
211        events
212    }
213}
214
215impl Node {
216    pub fn uri(&self) -> String {
217        use Node::*;
218
219        match self {
220            Mapping(_) => "tag:yaml.org,2002:map",
221            Sequence(_) => "tag:yaml.org,2002:seq",
222            String(_) => "tag:yaml.org,2002:str",
223            Null => "tag:yaml.org,2002:null",
224            Boolean(_) => "tag:yaml.org,2002:bool",
225            Integer(_) => "tag:yaml.org,2002:int",
226            FloatingPoint(_) => "tag:yaml.org,2002:float",
227        }
228        .into()
229    }
230
231    pub fn kind(&self) -> Kind {
232        use Node::*;
233
234        match self {
235            Mapping(_) => Kind::Mapping,
236            Sequence(_) => Kind::Sequence,
237            String(_) => Kind::Scalar,
238            Null => Kind::Scalar,
239            Boolean(_) => Kind::Scalar,
240            Integer(_) => Kind::Scalar,
241            FloatingPoint(_) => Kind::Scalar,
242        }
243    }
244
245    pub fn as_name(&self) -> Option<&String> {
246        use Node::*;
247
248        match self {
249            String(name) => Some(name),
250            _ => None,
251        }
252    }
253}
254
255#[derive(Debug)]
256pub enum Kind {
257    Sequence,
258    Mapping,
259    Scalar,
260}
261
262#[cfg(test)]
263mod test {
264    use crate::emitter::{Emitter, EmitterOptions};
265
266    use super::*;
267
268    #[test]
269    fn basic() {
270        let map = Mapping::from([
271            (
272                Node::String("clusterName".into()),
273                Node::String("opensearch-cluster".into()),
274            ),
275            (
276                Node::String("nodeGroup".into()),
277                Node::String("master".into()),
278            ),
279            (Node::String("singleNode".into()), Node::Boolean(false)),
280            (
281                Node::String("roles".into()),
282                Node::Sequence(Sequence::from([
283                    Node::String("master".into()),
284                    Node::String("ingest".into()),
285                ])),
286            ),
287            // (Node::String("replicas".into()), Node::Integer(3)),
288        ]);
289
290        let doc = Document::from_mapping(map);
291
292        let mut stream = Stream::new();
293        stream.push_document(doc);
294
295        let events = stream.into_events();
296        let mut output = String::new();
297
298        let emitter = Emitter::new(events, EmitterOptions::default());
299        emitter.emit(&mut output).unwrap();
300
301        // println!("{events:?}");
302        println!("{output}")
303    }
304}