#![doc = include_str!("../README.md")]
#![doc(html_root_url = "https://docs.rs/libyaml-safer/0.1.0")]
#![warn(clippy::pedantic)]
#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::fn_params_excessive_bools,
clippy::manual_range_contains,
clippy::missing_panics_doc,
clippy::missing_errors_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::needless_pass_by_value,
clippy::struct_excessive_bools,
clippy::too_many_arguments,
clippy::too_many_lines,
clippy::unnecessary_wraps,
clippy::match_wildcard_for_single_variants
)]
#![deny(unsafe_code)]
extern crate alloc;
#[macro_use]
mod macros;
mod document;
mod emitter;
mod error;
mod event;
mod parser;
mod reader;
mod scanner;
mod token;
pub use crate::document::*;
pub use crate::emitter::*;
pub use crate::error::*;
pub use crate::event::*;
pub use crate::parser::*;
pub use crate::scanner::*;
pub use crate::token::*;
pub(crate) const INPUT_RAW_BUFFER_SIZE: usize = 16384;
pub(crate) const INPUT_BUFFER_SIZE: usize = INPUT_RAW_BUFFER_SIZE;
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
pub const NULL_TAG: &str = "tag:yaml.org,2002:null";
pub const BOOL_TAG: &str = "tag:yaml.org,2002:bool";
pub const STR_TAG: &str = "tag:yaml.org,2002:str";
pub const INT_TAG: &str = "tag:yaml.org,2002:int";
pub const FLOAT_TAG: &str = "tag:yaml.org,2002:float";
pub const TIMESTAMP_TAG: &str = "tag:yaml.org,2002:timestamp";
pub const SEQ_TAG: &str = "tag:yaml.org,2002:seq";
pub const MAP_TAG: &str = "tag:yaml.org,2002:map";
pub const DEFAULT_SCALAR_TAG: &str = STR_TAG;
pub const DEFAULT_SEQUENCE_TAG: &str = SEQ_TAG;
pub const DEFAULT_MAPPING_TAG: &str = MAP_TAG;
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct VersionDirective {
pub major: i32,
pub minor: i32,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct TagDirective {
pub handle: String,
pub prefix: String,
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum Encoding {
#[default]
Any = 0,
Utf8 = 1,
Utf16Le = 2,
Utf16Be = 3,
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum Break {
#[default]
Any = 0,
Cr = 1,
Ln = 2,
CrLn = 3,
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum ScalarStyle {
#[default]
Any = 0,
Plain = 1,
SingleQuoted = 2,
DoubleQuoted = 3,
Literal = 4,
Folded = 5,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum SequenceStyle {
Any = 0,
Block = 1,
Flow = 2,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum MappingStyle {
Any = 0,
Block = 1,
Flow = 2,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanity() {
const SANITY_INPUT: &str = r#"unicode: "Sosa did fine.\u263A"
control: "\b1998\t1999\t2000\n"
hex esc: "\x0d\x0a is \r\n"
single: '"Howdy!" he cried.'
quoted: ' # Not a ''comment''.'
tie-fighter: '|\-*-/|'
"#;
const SANITY_OUTPUT: &str = r#"unicode: "Sosa did fine.\u263A"
control: "\b1998\t1999\t2000\n"
hex esc: "\r\n is \r\n"
single: '"Howdy!" he cried.'
quoted: ' # Not a ''comment''.'
tie-fighter: '|\-*-/|'
"#;
let mut parser = Parser::new();
let mut read_in = SANITY_INPUT.as_bytes();
parser.set_input_string(&mut read_in);
let doc = Document::load(&mut parser).unwrap();
let mut emitter = Emitter::new();
let mut output = Vec::new();
emitter.set_output(&mut output);
doc.dump(&mut emitter).unwrap();
let output_str = core::str::from_utf8(&output).expect("invalid UTF-8");
assert_eq!(output_str, SANITY_OUTPUT);
}
#[test]
fn scanner_marks() {
const INPUT: &str = "b:
c: true";
let mut scanner = Scanner::new();
let mut read_in = INPUT.as_bytes();
scanner.set_input(&mut read_in);
let events = scanner.collect::<Result<Vec<_>, _>>().unwrap();
let expected = &[
Token {
data: TokenData::StreamStart {
encoding: Encoding::Utf8,
},
start_mark: Mark {
index: 0,
line: 0,
column: 0,
},
end_mark: Mark {
index: 0,
line: 0,
column: 0,
},
},
Token {
data: TokenData::BlockMappingStart,
start_mark: Mark {
index: 0,
line: 0,
column: 0,
},
end_mark: Mark {
index: 0,
line: 0,
column: 0,
},
},
Token {
data: TokenData::Key,
start_mark: Mark {
index: 0,
line: 0,
column: 0,
},
end_mark: Mark {
index: 0,
line: 0,
column: 0,
},
},
Token {
data: TokenData::Scalar {
value: String::from("b"),
style: ScalarStyle::Plain,
},
start_mark: Mark {
index: 0,
line: 0,
column: 0,
},
end_mark: Mark {
index: 1,
line: 0,
column: 1,
},
},
Token {
data: TokenData::Value,
start_mark: Mark {
index: 1,
line: 0,
column: 1,
},
end_mark: Mark {
index: 2,
line: 0,
column: 2,
},
},
Token {
data: TokenData::Key,
start_mark: Mark {
index: 3,
line: 1,
column: 0,
},
end_mark: Mark {
index: 3,
line: 1,
column: 0,
},
},
Token {
data: TokenData::Scalar {
value: String::from("c"),
style: ScalarStyle::Plain,
},
start_mark: Mark {
index: 3,
line: 1,
column: 0,
},
end_mark: Mark {
index: 4,
line: 1,
column: 1,
},
},
Token {
data: TokenData::Value,
start_mark: Mark {
index: 4,
line: 1,
column: 1,
},
end_mark: Mark {
index: 5,
line: 1,
column: 2,
},
},
Token {
data: TokenData::Scalar {
value: String::from("true"),
style: ScalarStyle::Plain,
},
start_mark: Mark {
index: 6,
line: 1,
column: 3,
},
end_mark: Mark {
index: 10,
line: 1,
column: 7,
},
},
Token {
data: TokenData::BlockEnd,
start_mark: Mark {
index: 10,
line: 2,
column: 0,
},
end_mark: Mark {
index: 10,
line: 2,
column: 0,
},
},
Token {
data: TokenData::StreamEnd,
start_mark: Mark {
index: 10,
line: 2,
column: 0,
},
end_mark: Mark {
index: 10,
line: 2,
column: 0,
},
},
];
assert_eq!(
events,
expected,
"diff:\n{}",
zip_longest(
format!("{events:#?}").lines(),
format!("{expected:#?}").lines()
)
.map(|(a, b)| {
let a = a.unwrap_or_default();
let b = b.unwrap_or_default();
format!("{a:<40} {b}")
})
.collect::<Vec<_>>()
.join("\n")
);
}
fn zip_longest<A: Iterator, B: Iterator>(
a: A,
b: B,
) -> impl Iterator<Item = (Option<A::Item>, Option<B::Item>)> {
let mut a = a.map(Some).collect::<Vec<_>>();
let mut b = b.map(Some).collect::<Vec<_>>();
let len = a.len().max(b.len());
a.resize_with(len, || None);
b.resize_with(len, || None);
a.into_iter()
.zip(b)
.take_while(|(a, b)| a.is_some() || b.is_some())
}
}