libyaml_safer/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(html_root_url = "https://docs.rs/libyaml-safer/0.1.0")]
3#![warn(clippy::pedantic)]
4#![allow(
5    clippy::cast_lossless,
6    clippy::cast_possible_truncation,
7    clippy::cast_possible_wrap,
8    clippy::cast_sign_loss,
9    clippy::fn_params_excessive_bools,
10    clippy::manual_range_contains,
11    clippy::missing_panics_doc,
12    clippy::missing_errors_doc,
13    clippy::module_name_repetitions,
14    clippy::must_use_candidate,
15    clippy::needless_pass_by_value,
16    clippy::struct_excessive_bools,
17    clippy::too_many_arguments,
18    clippy::too_many_lines,
19    clippy::unnecessary_wraps,
20    clippy::match_wildcard_for_single_variants
21)]
22#![deny(unsafe_code)]
23
24extern crate alloc;
25
26#[macro_use]
27mod macros;
28
29mod document;
30mod emitter;
31mod error;
32mod event;
33mod parser;
34mod reader;
35mod scanner;
36mod token;
37
38pub use crate::document::*;
39pub use crate::emitter::*;
40pub use crate::error::*;
41pub use crate::event::*;
42pub use crate::parser::*;
43pub use crate::scanner::*;
44pub use crate::token::*;
45
46pub(crate) const INPUT_RAW_BUFFER_SIZE: usize = 16384;
47pub(crate) const INPUT_BUFFER_SIZE: usize = INPUT_RAW_BUFFER_SIZE;
48pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
49
50/// The tag `!!null` with the only possible value: `null`.
51pub const NULL_TAG: &str = "tag:yaml.org,2002:null";
52/// The tag `!!bool` with the values: `true` and `false`.
53pub const BOOL_TAG: &str = "tag:yaml.org,2002:bool";
54/// The tag `!!str` for string values.
55pub const STR_TAG: &str = "tag:yaml.org,2002:str";
56/// The tag `!!int` for integer values.
57pub const INT_TAG: &str = "tag:yaml.org,2002:int";
58/// The tag `!!float` for float values.
59pub const FLOAT_TAG: &str = "tag:yaml.org,2002:float";
60/// The tag `!!timestamp` for date and time values.
61pub const TIMESTAMP_TAG: &str = "tag:yaml.org,2002:timestamp";
62
63/// The tag `!!seq` is used to denote sequences.
64pub const SEQ_TAG: &str = "tag:yaml.org,2002:seq";
65/// The tag `!!map` is used to denote mapping.
66pub const MAP_TAG: &str = "tag:yaml.org,2002:map";
67
68/// The default scalar tag is `!!str`.
69pub const DEFAULT_SCALAR_TAG: &str = STR_TAG;
70/// The default sequence tag is `!!seq`.
71pub const DEFAULT_SEQUENCE_TAG: &str = SEQ_TAG;
72/// The default mapping tag is `!!map`.
73pub const DEFAULT_MAPPING_TAG: &str = MAP_TAG;
74
75/// The version directive data.
76#[derive(Clone, Copy, Debug, PartialEq)]
77#[non_exhaustive]
78pub struct VersionDirective {
79    /// The major version number.
80    pub major: i32,
81    /// The minor version number.
82    pub minor: i32,
83}
84
85/// The tag directive data.
86#[derive(Debug, Clone, PartialEq)]
87#[non_exhaustive]
88pub struct TagDirective {
89    /// The tag handle.
90    pub handle: String,
91    /// The tag prefix.
92    pub prefix: String,
93}
94
95/// The stream encoding.
96#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
97#[non_exhaustive]
98pub enum Encoding {
99    /// Let the parser choose the encoding.
100    #[default]
101    Any = 0,
102    /// The default UTF-8 encoding.
103    Utf8 = 1,
104    /// The UTF-16-LE encoding with BOM.
105    Utf16Le = 2,
106    /// The UTF-16-BE encoding with BOM.
107    Utf16Be = 3,
108}
109
110/// Line break type.
111#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
112#[non_exhaustive]
113pub enum Break {
114    /// Let the parser choose the break type.
115    #[default]
116    Any = 0,
117    /// Use CR for line breaks (Mac style).
118    Cr = 1,
119    /// Use LN for line breaks (Unix style).
120    Ln = 2,
121    /// Use CR LN for line breaks (DOS style).
122    CrLn = 3,
123}
124
125/// Scalar styles.
126#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
127#[non_exhaustive]
128pub enum ScalarStyle {
129    /// Let the emitter choose the style.
130    #[default]
131    Any = 0,
132    /// The plain scalar style.
133    Plain = 1,
134    /// The single-quoted scalar style.
135    SingleQuoted = 2,
136    /// The double-quoted scalar style.
137    DoubleQuoted = 3,
138    /// The literal scalar style.
139    Literal = 4,
140    /// The folded scalar style.
141    Folded = 5,
142}
143
144/// Sequence styles.
145#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
146#[non_exhaustive]
147pub enum SequenceStyle {
148    /// Let the emitter choose the style.
149    Any = 0,
150    /// The block sequence style.
151    Block = 1,
152    /// The flow sequence style.
153    Flow = 2,
154}
155
156/// Mapping styles.
157#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
158#[non_exhaustive]
159pub enum MappingStyle {
160    /// Let the emitter choose the style.
161    Any = 0,
162    /// The block mapping style.
163    Block = 1,
164    /// The flow mapping style.
165    Flow = 2,
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn sanity() {
174        const SANITY_INPUT: &str = r#"unicode: "Sosa did fine.\u263A"
175control: "\b1998\t1999\t2000\n"
176hex esc: "\x0d\x0a is \r\n"
177
178single: '"Howdy!" he cried.'
179quoted: ' # Not a ''comment''.'
180tie-fighter: '|\-*-/|'
181"#;
182        const SANITY_OUTPUT: &str = r#"unicode: "Sosa did fine.\u263A"
183control: "\b1998\t1999\t2000\n"
184hex esc: "\r\n is \r\n"
185single: '"Howdy!" he cried.'
186quoted: ' # Not a ''comment''.'
187tie-fighter: '|\-*-/|'
188"#;
189        let mut parser = Parser::new();
190        let mut read_in = SANITY_INPUT.as_bytes();
191        parser.set_input_string(&mut read_in);
192        let doc = Document::load(&mut parser).unwrap();
193
194        let mut emitter = Emitter::new();
195        let mut output = Vec::new();
196        emitter.set_output(&mut output);
197        doc.dump(&mut emitter).unwrap();
198        let output_str = core::str::from_utf8(&output).expect("invalid UTF-8");
199        assert_eq!(output_str, SANITY_OUTPUT);
200    }
201
202    #[test]
203    fn scanner_marks() {
204        const INPUT: &str = "b:
205c: true";
206        let mut scanner = Scanner::new();
207        let mut read_in = INPUT.as_bytes();
208        scanner.set_input(&mut read_in);
209        let events = scanner.collect::<Result<Vec<_>, _>>().unwrap();
210        let expected = &[
211            Token {
212                data: TokenData::StreamStart {
213                    encoding: Encoding::Utf8,
214                },
215                start_mark: Mark {
216                    index: 0,
217                    line: 0,
218                    column: 0,
219                },
220                end_mark: Mark {
221                    index: 0,
222                    line: 0,
223                    column: 0,
224                },
225            },
226            Token {
227                data: TokenData::BlockMappingStart,
228                start_mark: Mark {
229                    index: 0,
230                    line: 0,
231                    column: 0,
232                },
233                end_mark: Mark {
234                    index: 0,
235                    line: 0,
236                    column: 0,
237                },
238            },
239            Token {
240                data: TokenData::Key,
241                start_mark: Mark {
242                    index: 0,
243                    line: 0,
244                    column: 0,
245                },
246                end_mark: Mark {
247                    index: 0,
248                    line: 0,
249                    column: 0,
250                },
251            },
252            Token {
253                data: TokenData::Scalar {
254                    value: String::from("b"),
255                    style: ScalarStyle::Plain,
256                },
257                start_mark: Mark {
258                    index: 0,
259                    line: 0,
260                    column: 0,
261                },
262                end_mark: Mark {
263                    index: 1,
264                    line: 0,
265                    column: 1,
266                },
267            },
268            Token {
269                data: TokenData::Value,
270                start_mark: Mark {
271                    index: 1,
272                    line: 0,
273                    column: 1,
274                },
275                end_mark: Mark {
276                    index: 2,
277                    line: 0,
278                    column: 2,
279                },
280            },
281            Token {
282                data: TokenData::Key,
283                start_mark: Mark {
284                    index: 3,
285                    line: 1,
286                    column: 0,
287                },
288                end_mark: Mark {
289                    index: 3,
290                    line: 1,
291                    column: 0,
292                },
293            },
294            Token {
295                data: TokenData::Scalar {
296                    value: String::from("c"),
297                    style: ScalarStyle::Plain,
298                },
299                start_mark: Mark {
300                    index: 3,
301                    line: 1,
302                    column: 0,
303                },
304                end_mark: Mark {
305                    index: 4,
306                    line: 1,
307                    column: 1,
308                },
309            },
310            Token {
311                data: TokenData::Value,
312                start_mark: Mark {
313                    index: 4,
314                    line: 1,
315                    column: 1,
316                },
317                end_mark: Mark {
318                    index: 5,
319                    line: 1,
320                    column: 2,
321                },
322            },
323            Token {
324                data: TokenData::Scalar {
325                    value: String::from("true"),
326                    style: ScalarStyle::Plain,
327                },
328                start_mark: Mark {
329                    index: 6,
330                    line: 1,
331                    column: 3,
332                },
333                end_mark: Mark {
334                    index: 10,
335                    line: 1,
336                    column: 7,
337                },
338            },
339            Token {
340                data: TokenData::BlockEnd,
341                start_mark: Mark {
342                    index: 10,
343                    line: 2,
344                    column: 0,
345                },
346                end_mark: Mark {
347                    index: 10,
348                    line: 2,
349                    column: 0,
350                },
351            },
352            Token {
353                data: TokenData::StreamEnd,
354                start_mark: Mark {
355                    index: 10,
356                    line: 2,
357                    column: 0,
358                },
359                end_mark: Mark {
360                    index: 10,
361                    line: 2,
362                    column: 0,
363                },
364            },
365        ];
366        assert_eq!(
367            events,
368            expected,
369            "diff:\n{}",
370            zip_longest(
371                format!("{events:#?}").lines(),
372                format!("{expected:#?}").lines()
373            )
374            .map(|(a, b)| {
375                let a = a.unwrap_or_default();
376                let b = b.unwrap_or_default();
377                format!("{a:<40} {b}")
378            })
379            .collect::<Vec<_>>()
380            .join("\n")
381        );
382    }
383
384    fn zip_longest<A: Iterator, B: Iterator>(
385        a: A,
386        b: B,
387    ) -> impl Iterator<Item = (Option<A::Item>, Option<B::Item>)> {
388        let mut a = a.map(Some).collect::<Vec<_>>();
389        let mut b = b.map(Some).collect::<Vec<_>>();
390        let len = a.len().max(b.len());
391        a.resize_with(len, || None);
392        b.resize_with(len, || None);
393        a.into_iter()
394            .zip(b)
395            .take_while(|(a, b)| a.is_some() || b.is_some())
396    }
397}