#![warn(clippy::all, clippy::pedantic)]
mod bytes;
mod errors;
mod parse;
mod tests;
pub use crate::errors::YamlParseError;
pub(crate) type Result<T> = std::result::Result<T, YamlParseError>;
use parse::Parser;
use std::{fmt, fmt::Display};
#[cfg_attr(test, derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Yaml<'a> {
Scalar(&'a str),
Sequence(Vec<Yaml<'a>>),
Mapping(Vec<Entry<'a>>),
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum PrintStyle {
Block,
#[allow(unused)]
Flow,
}
fn print_indent(indent: usize, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:indent$}", "", indent = indent)
}
fn print_yaml(
node: &Yaml<'_>,
indent: usize,
f: &mut fmt::Formatter,
style: PrintStyle,
) -> fmt::Result {
const INDENT_AMT: usize = 2;
match node {
Yaml::Scalar(slice) => write!(f, "{}", slice),
Yaml::Sequence(seq) => {
match style {
PrintStyle::Block => {
for el in seq.iter() {
print_indent(indent, f)?;
write!(f, "-")?;
match el {
Yaml::Scalar(slice) => writeln!(f, " {scal}", scal = slice)?,
Yaml::Sequence(..) | Yaml::Mapping(..) => {
#[allow(clippy::write_with_newline)]
write!(f, "\n")?;
print_yaml(el, indent + INDENT_AMT, f, style)?;
}
}
}
}
PrintStyle::Flow => {
write!(f, "[ ")?;
let last_idx = seq.len() - 1;
for (idx, elem) in seq.iter().enumerate() {
if idx == last_idx {
write!(f, "{}", elem)?;
} else {
write!(f, "{}, ", elem)?;
}
}
write!(f, " ]")?;
}
}
Ok(())
}
Yaml::Mapping(map) => {
match style {
PrintStyle::Block => {
for entry in map.iter() {
match &entry.key {
Yaml::Scalar(..) => {
print_indent(indent, f)?;
print_yaml(&entry.key, indent, f, PrintStyle::Block)?;
write!(f, " ")?;
}
Yaml::Sequence(..) | Yaml::Mapping(..) => {
print_yaml(&entry.key, indent + INDENT_AMT, f, PrintStyle::Block)?;
print_indent(indent, f)?;
}
}
write!(f, ":")?;
match &entry.value {
Yaml::Scalar(..) => {
write!(f, " ")?;
print_yaml(&entry.value, indent, f, PrintStyle::Block)?;
#[allow(clippy::write_with_newline)]
write!(f, "\n")?;
}
Yaml::Sequence(..) | Yaml::Mapping(..) => {
#[allow(clippy::write_with_newline)]
write!(f, "\n")?;
print_yaml(&entry.value, indent + INDENT_AMT, f, PrintStyle::Block)?
}
}
}
}
PrintStyle::Flow => {
write!(f, "{{")?;
let last_idx = map.len() - 1;
for (idx, entry) in map.iter().enumerate() {
if idx == last_idx {
write!(f, "{}", entry)?;
} else {
write!(f, "{}, ", entry)?;
}
}
write!(f, "}}")?;
}
}
Ok(())
}
}
}
impl Display for Yaml<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
print_yaml(&self, 0, f, PrintStyle::Block)
}
}
#[cfg_attr(test, derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Entry<'a> {
#[cfg_attr(test, serde(borrow))]
pub key: Yaml<'a>,
#[cfg_attr(test, serde(borrow))]
pub value: Yaml<'a>,
}
impl<'a> Entry<'a> {
#[allow(clippy::must_use_candidate)]
pub fn new(key: Yaml<'a>, value: Yaml<'a>) -> Self {
Self { key, value }
}
}
impl<'a> Display for Entry<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} : {}", self.key, self.value)
}
}
pub fn parse(input: &str) -> Result<Yaml<'_>> {
let mut parser = Parser::new(input)?;
parser.parse()
}