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
50pub const NULL_TAG: &str = "tag:yaml.org,2002:null";
52pub const BOOL_TAG: &str = "tag:yaml.org,2002:bool";
54pub const STR_TAG: &str = "tag:yaml.org,2002:str";
56pub const INT_TAG: &str = "tag:yaml.org,2002:int";
58pub const FLOAT_TAG: &str = "tag:yaml.org,2002:float";
60pub const TIMESTAMP_TAG: &str = "tag:yaml.org,2002:timestamp";
62
63pub const SEQ_TAG: &str = "tag:yaml.org,2002:seq";
65pub const MAP_TAG: &str = "tag:yaml.org,2002:map";
67
68pub const DEFAULT_SCALAR_TAG: &str = STR_TAG;
70pub const DEFAULT_SEQUENCE_TAG: &str = SEQ_TAG;
72pub const DEFAULT_MAPPING_TAG: &str = MAP_TAG;
74
75#[derive(Clone, Copy, Debug, PartialEq)]
77#[non_exhaustive]
78pub struct VersionDirective {
79 pub major: i32,
81 pub minor: i32,
83}
84
85#[derive(Debug, Clone, PartialEq)]
87#[non_exhaustive]
88pub struct TagDirective {
89 pub handle: String,
91 pub prefix: String,
93}
94
95#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
97#[non_exhaustive]
98pub enum Encoding {
99 #[default]
101 Any = 0,
102 Utf8 = 1,
104 Utf16Le = 2,
106 Utf16Be = 3,
108}
109
110#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
112#[non_exhaustive]
113pub enum Break {
114 #[default]
116 Any = 0,
117 Cr = 1,
119 Ln = 2,
121 CrLn = 3,
123}
124
125#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
127#[non_exhaustive]
128pub enum ScalarStyle {
129 #[default]
131 Any = 0,
132 Plain = 1,
134 SingleQuoted = 2,
136 DoubleQuoted = 3,
138 Literal = 4,
140 Folded = 5,
142}
143
144#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
146#[non_exhaustive]
147pub enum SequenceStyle {
148 Any = 0,
150 Block = 1,
152 Flow = 2,
154}
155
156#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
158#[non_exhaustive]
159pub enum MappingStyle {
160 Any = 0,
162 Block = 1,
164 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}