mod lexer;
mod parser;
pub use parser::{Error as ParseError, ErrorKind as ParseErrorKind};
use {crate::CowStr, core::fmt::Display, lexer::Token, logos::Logos};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Directive<'a> {
Package(CowStr<'a>),
Section {
type_: CowStr<'a>,
name: Option<CowStr<'a>>,
},
Option {
key: CowStr<'a>,
value: Option<CowStr<'a>>,
},
List {
key: CowStr<'a>,
value: CowStr<'a>,
},
}
impl<'a> Directive<'a> {
pub fn next_from_str(string: &mut &'a str) -> Result<Option<Self>, ParseError> {
let mut lexer = Token::lexer(string);
let parse_result = parser::Parser { lexer: &mut lexer }.run();
if parse_result.is_ok() {
*string = lexer.remainder();
}
parse_result
}
}
impl Display for Directive<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let (directive, key, value) = match self {
Directive::Package(s) => ("package", s, None),
Directive::Section { type_, name } => ("config", type_, name.as_ref()),
Directive::Option { key, value } => ("\toption", key, value.as_ref()),
Directive::List { key, value } => ("\tlist", key, Some(value)),
};
write!(f, "{directive} \"{}\"", key.escape_default())?;
if let Some(value) = value {
write!(f, " \"{}\"", value.escape_default())?;
}
Ok(())
}
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct DirectiveIter<'a> {
input: &'a str,
}
impl<'a> DirectiveIter<'a> {
pub const fn new(input: &'a str) -> Self {
Self { input }
}
}
impl<'a> Iterator for DirectiveIter<'a> {
type Item = Result<Directive<'a>, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
Directive::next_from_str(&mut self.input).transpose()
}
}
impl<'a> DirectiveIter<'a> {
#[must_use]
pub const fn remainder(&self) -> &'a str {
self.input
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_directives() {
let mut input = r#"
package ':3'
config type 0name
option 'novalue'
list "has" value
"#;
let mut next = || Directive::next_from_str(&mut input).unwrap();
assert_eq!(next(), Some(Directive::Package(":3".into())));
assert_eq!(
next(),
Some(Directive::Section {
type_: "type".into(),
name: Some("0name".into())
})
);
assert_eq!(
next(),
Some(Directive::Option {
key: "novalue".into(),
value: None
})
);
assert_eq!(
next(),
Some(Directive::List {
key: "has".into(),
value: "value".into()
})
);
assert_eq!(next(), None);
}
#[test]
fn display() {
use core::fmt::Write as _;
let directives = [
Directive::Package(":3".into()),
Directive::Section {
type_: "kitty".into(),
name: Some("emily".into()),
},
Directive::Option {
key: "color".into(),
value: Some("orange".into()),
},
Directive::List {
key: "traits".into(),
value: "Sync".into(),
},
];
let mut buf = alloc::string::String::new();
directives
.iter()
.try_for_each(|dir| writeln!(&mut buf, "{dir}"))
.unwrap();
DirectiveIter::new(&buf)
.zip(directives)
.for_each(|(parsed, expected)| assert_eq!(parsed, Ok(expected)));
}
}