diff --git a/src/lib.rs b/src/lib.rs index 4d8dfd8..109517e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use nom::combinator::all_consuming; use nom::Finish; pub use crate::error::{KdlError, KdlErrorKind, TryFromKdlNodeValueError}; -pub use crate::node::{KdlNode, KdlNodeValue}; +pub use crate::node::{KdlNode, KdlValue}; mod error; mod node; diff --git a/src/node.rs b/src/node.rs index 6dfdf2a..e5d979d 100644 --- a/src/node.rs +++ b/src/node.rs @@ -5,13 +5,13 @@ use crate::TryFromKdlNodeValueError; #[derive(Debug, Clone, PartialEq)] pub struct KdlNode { pub name: String, - pub values: Vec, - pub properties: HashMap, + pub values: Vec, + pub properties: HashMap, pub children: Vec, } #[derive(Debug, Clone, PartialEq)] -pub enum KdlNodeValue { +pub enum KdlValue { Int(i64), Float(f64), String(String), @@ -21,42 +21,42 @@ pub enum KdlNodeValue { // Support conversions from base types into KdlNodeValue -impl From for KdlNodeValue { +impl From for KdlValue { fn from(v: i64) -> Self { Self::Int(v) } } -impl From for KdlNodeValue { +impl From for KdlValue { fn from(v: f64) -> Self { Self::Float(v) } } -impl From for KdlNodeValue { +impl From for KdlValue { fn from(v: String) -> Self { Self::String(v) } } -impl From<&str> for KdlNodeValue { +impl From<&str> for KdlValue { fn from(v: &str) -> Self { Self::String(v.to_owned()) } } -impl From for KdlNodeValue { +impl From for KdlValue { fn from(v: bool) -> Self { Self::Boolean(v) } } -impl From> for KdlNodeValue +impl From> for KdlValue where - T: Into, + T: Into, { fn from(v: Option) -> Self { - v.map_or(KdlNodeValue::Null, |v| v.into()) + v.map_or(KdlValue::Null, |v| v.into()) } } @@ -74,11 +74,11 @@ macro_rules! impl_try_from { fn try_from(value: $source) -> Result { match value { $( $good => Ok($value), )+ - $( KdlNodeValue::$bad(_) => Err(TryFromKdlNodeValueError { + $( KdlValue::$bad(_) => Err(TryFromKdlNodeValueError { expected: stringify!($typ), variant: stringify!($bad) }), )+ - KdlNodeValue::Null => Err(TryFromKdlNodeValueError { + KdlValue::Null => Err(TryFromKdlNodeValueError { expected: stringify!($typ), variant: "Null" }), @@ -90,31 +90,31 @@ macro_rules! impl_try_from { fn try_from(value: $source) -> Result { match value { $( $good => Ok(Some($value)), )+ - $( KdlNodeValue::$bad(_) => Err(TryFromKdlNodeValueError { + $( KdlValue::$bad(_) => Err(TryFromKdlNodeValueError { expected: concat!("Option::<", stringify!($typ), ">"), variant: stringify!($bad) }), )+ - KdlNodeValue::Null => Ok(None), + KdlValue::Null => Ok(None), } } } }; (& $($lt:lifetime)?, $typ:ty, $($tt:tt)*) => { - impl_try_from!(<$($lt)?> & $($lt)? KdlNodeValue => $typ, $($tt)*); + impl_try_from!(<$($lt)?> & $($lt)? KdlValue => $typ, $($tt)*); }; ($typ:ty, $($tt:tt)*) => { - impl_try_from!(<> KdlNodeValue => $typ, $($tt)*); + impl_try_from!(<> KdlValue => $typ, $($tt)*); }; } -impl_try_from!(i64, KdlNodeValue::Int(v) => v; Float, String, Boolean); -impl_try_from!(&, i64, KdlNodeValue::Int(v) => *v; Float, String, Boolean); -impl_try_from!(f64, KdlNodeValue::Float(v) => v; Int, String, Boolean); -impl_try_from!(&, f64, KdlNodeValue::Float(v) => *v; Int, String, Boolean); -impl_try_from!(String, KdlNodeValue::String(v) => v; Int, Float, Boolean); -impl_try_from!(&'a, &'a str, KdlNodeValue::String(v) => &v[..]; Int, Float, Boolean); -impl_try_from!(bool, KdlNodeValue::Boolean(v) => v; Int, Float, String); -impl_try_from!(&, bool, KdlNodeValue::Boolean(v) => *v; Int, Float, String); +impl_try_from!(i64, KdlValue::Int(v) => v; Float, String, Boolean); +impl_try_from!(&, i64, KdlValue::Int(v) => *v; Float, String, Boolean); +impl_try_from!(f64, KdlValue::Float(v) => v; Int, String, Boolean); +impl_try_from!(&, f64, KdlValue::Float(v) => *v; Int, String, Boolean); +impl_try_from!(String, KdlValue::String(v) => v; Int, Float, Boolean); +impl_try_from!(&'a, &'a str, KdlValue::String(v) => &v[..]; Int, Float, Boolean); +impl_try_from!(bool, KdlValue::Boolean(v) => v; Int, Float, String); +impl_try_from!(&, bool, KdlValue::Boolean(v) => *v; Int, Float, String); #[cfg(test)] mod tests { @@ -122,41 +122,41 @@ mod tests { #[test] fn from() { - assert_eq!(KdlNodeValue::from(1), KdlNodeValue::Int(1)); - assert_eq!(KdlNodeValue::from(1.5), KdlNodeValue::Float(1.5)); + assert_eq!(KdlValue::from(1), KdlValue::Int(1)); + assert_eq!(KdlValue::from(1.5), KdlValue::Float(1.5)); assert_eq!( - KdlNodeValue::from("foo".to_owned()), - KdlNodeValue::String("foo".to_owned()) + KdlValue::from("foo".to_owned()), + KdlValue::String("foo".to_owned()) ); assert_eq!( - KdlNodeValue::from("bar"), - KdlNodeValue::String("bar".to_owned()) + KdlValue::from("bar"), + KdlValue::String("bar".to_owned()) ); - assert_eq!(KdlNodeValue::from(true), KdlNodeValue::Boolean(true)); + assert_eq!(KdlValue::from(true), KdlValue::Boolean(true)); - assert_eq!(KdlNodeValue::from(None::), KdlNodeValue::Null); - assert_eq!(KdlNodeValue::from(Some(1)), KdlNodeValue::Int(1)); + assert_eq!(KdlValue::from(None::), KdlValue::Null); + assert_eq!(KdlValue::from(Some(1)), KdlValue::Int(1)); } #[test] fn try_from_success() { - assert_eq!(i64::try_from(KdlNodeValue::Int(1)), Ok(1)); - assert_eq!(i64::try_from(&KdlNodeValue::Int(1)), Ok(1)); - assert_eq!(f64::try_from(KdlNodeValue::Float(1.5)), Ok(1.5)); - assert_eq!(f64::try_from(&KdlNodeValue::Float(1.5)), Ok(1.5)); + assert_eq!(i64::try_from(KdlValue::Int(1)), Ok(1)); + assert_eq!(i64::try_from(&KdlValue::Int(1)), Ok(1)); + assert_eq!(f64::try_from(KdlValue::Float(1.5)), Ok(1.5)); + assert_eq!(f64::try_from(&KdlValue::Float(1.5)), Ok(1.5)); assert_eq!( - String::try_from(KdlNodeValue::String("foo".to_owned())), + String::try_from(KdlValue::String("foo".to_owned())), Ok("foo".to_owned()) ); assert_eq!( - <&str as TryFrom<_>>::try_from(&KdlNodeValue::String("foo".to_owned())), + <&str as TryFrom<_>>::try_from(&KdlValue::String("foo".to_owned())), Ok("foo") ); - assert_eq!(bool::try_from(KdlNodeValue::Boolean(true)), Ok(true)); - assert_eq!(bool::try_from(&KdlNodeValue::Boolean(true)), Ok(true)); + assert_eq!(bool::try_from(KdlValue::Boolean(true)), Ok(true)); + assert_eq!(bool::try_from(&KdlValue::Boolean(true)), Ok(true)); - assert_eq!(Option::::try_from(KdlNodeValue::Int(1)), Ok(Some(1))); - assert_eq!(Option::::try_from(KdlNodeValue::Null), Ok(None)); + assert_eq!(Option::::try_from(KdlValue::Int(1)), Ok(Some(1))); + assert_eq!(Option::::try_from(KdlValue::Null), Ok(None)); } #[test] @@ -164,13 +164,13 @@ mod tests { // We don't expose the internal format of the error type, so let's just test the message // for a couple of cases. assert_eq!( - format!("{}", i64::try_from(KdlNodeValue::Float(1.5)).unwrap_err()), + format!("{}", i64::try_from(KdlValue::Float(1.5)).unwrap_err()), "Failed to convert from KdlNodeValue::Float to i64." ); assert_eq!( format!( "{}", - Option::::try_from(KdlNodeValue::Float(1.5)).unwrap_err() + Option::::try_from(KdlValue::Float(1.5)).unwrap_err() ), "Failed to convert from KdlNodeValue::Float to Option::." ); diff --git a/src/parser.rs b/src/parser.rs index 149f664..9ef283a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,7 @@ use nom::Finish; use nom::IResult; use crate::error::KdlParseError; -use crate::node::{KdlNode, KdlNodeValue}; +use crate::node::{KdlNode, KdlValue}; /// `nodes := linespace* node* linespace*` pub(crate) fn nodes(input: &str) -> IResult<&str, Vec, KdlParseError<&str>> { @@ -65,8 +65,8 @@ pub(crate) fn strip_trailing_newline(input: &str) -> &str { #[derive(Clone)] enum NodeArg { - Value(KdlNodeValue), - Property(String, KdlNodeValue), + Value(KdlValue), + Property(String, KdlValue), } /// `node := ('/-' ws*)? identifier (node-space node-props-and-args)* node-space* (node-terminator | node-children)` @@ -75,10 +75,7 @@ pub(crate) fn node(input: &str) -> IResult<&str, Option, KdlParseError< let (input, tag) = identifier(input)?; let (input, args) = many0(preceded(node_space, node_prop_or_arg))(input)?; let (input, _) = many0(node_space)(input)?; - let (input, children) = alt(( - value(Vec::new(), node_terminator), - node_children, - ))(input)?; + let (input, children) = alt((value(Vec::new(), node_terminator), node_children))(input)?; if comment.is_some() { Ok((input, None)) } else { @@ -141,7 +138,7 @@ fn node_prop_or_arg(input: &str) -> IResult<&str, Option, KdlParseError } /// `prop := identifier '=' value` -fn property(input: &str) -> IResult<&str, (String, KdlNodeValue), KdlParseError<&str>> { +fn property(input: &str) -> IResult<&str, (String, KdlValue), KdlParseError<&str>> { let (input, key) = identifier(input)?; let (input, _) = tag("=")(input)?; let (input, val) = node_value(input)?; @@ -149,19 +146,24 @@ fn property(input: &str) -> IResult<&str, (String, KdlNodeValue), KdlParseError< } /// `value := string | raw_string | number | boolean | 'null'` -fn node_value(input: &str) -> IResult<&str, KdlNodeValue, KdlParseError<&str>> { +fn node_value(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { alt(( - map(string, KdlNodeValue::String), - map(raw_string, |s| KdlNodeValue::String(s.into())), + map(string, KdlValue::String), + map(raw_string, |s| KdlValue::String(s.into())), number, boolean, - value(KdlNodeValue::Null, tag("null")), + value(KdlValue::Null, tag("null")), ))(input) } /// node-terminator := single-line-comment | newline | ';' | eof fn node_terminator(input: &str) -> IResult<&str, (), KdlParseError<&str>> { - alt((value((), eof), single_line_comment, newline, value((), char(';'))))(input) + alt(( + value((), eof), + single_line_comment, + newline, + value((), char(';')), + ))(input) } /// `node-children := ('/-' ws*)? '{' nodes '}'` @@ -231,13 +233,13 @@ fn raw_string(input: &str) -> IResult<&str, &str, KdlParseError<&str>> { } /// `number := decimal | hex | octal | binary` -fn number(input: &str) -> IResult<&str, KdlNodeValue, KdlParseError<&str>> { +fn number(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { alt(( - map(integer, KdlNodeValue::Int), - map(hexadecimal, KdlNodeValue::Int), - map(octal, KdlNodeValue::Int), - map(binary, KdlNodeValue::Int), - map(float, KdlNodeValue::Float), + map(integer, KdlValue::Int), + map(hexadecimal, KdlValue::Int), + map(octal, KdlValue::Int), + map(binary, KdlValue::Int), + map(float, KdlValue::Float), ))(input) } @@ -325,10 +327,10 @@ fn binary(input: &str) -> IResult<&str, i64, KdlParseError<&str>> { } /// `boolean := 'true' | 'false'` -fn boolean(input: &str) -> IResult<&str, KdlNodeValue, KdlParseError<&str>> { +fn boolean(input: &str) -> IResult<&str, KdlValue, KdlParseError<&str>> { alt(( - value(KdlNodeValue::Boolean(true), tag("true")), - value(KdlNodeValue::Boolean(false), tag("false")), + value(KdlValue::Boolean(true), tag("true")), + value(KdlValue::Boolean(false), tag("false")), ))(input) } @@ -387,6 +389,173 @@ fn newline(input: &str) -> IResult<&str, (), KdlParseError<&str>> { mod tests { use super::*; + #[test] + fn test_nodes() { + assert_eq!( + nodes("node"), + Ok(( + "", + vec![KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }] + )) + ); + assert_eq!( + nodes("node\n"), + Ok(( + "", + vec![KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }] + )) + ); + assert_eq!( + nodes("\nnode\n"), + Ok(( + "", + vec![KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }] + )) + ); + assert_eq!( + nodes("node1\nnode2"), + Ok(( + "", + vec![ + KdlNode { + name: "node1".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }, + KdlNode { + name: "node2".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + } + ] + )) + ); + } + + #[test] + fn test_node() { + assert_eq!( + node("node"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node\n"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node;"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node 1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: vec![KdlValue::Int(1)], + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node 1 2 \"3\" true false null"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: vec![ + KdlValue::Int(1), + KdlValue::Int(2), + KdlValue::String("3".into()), + KdlValue::Boolean(true), + KdlValue::Boolean(false), + KdlValue::Null + ], + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + + assert_eq!( + node("node {\n node2\n}"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: vec![KdlNode { + name: "node2".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new() + }] + }) + )) + ); + + assert_eq!( + node("node { node2; }"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: vec![KdlNode { + name: "node2".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new() + }] + }) + )) + ); + } + #[test] fn test_node_slashdash_comment() { assert_eq!(node("/-node"), Ok(("", None))); @@ -395,93 +564,168 @@ mod tests { assert_eq!(node("/-node 1 2 3"), Ok(("", None))); assert_eq!(node("/-node key=false"), Ok(("", None))); assert_eq!(node("/-node{\nnode\n}"), Ok(("", None))); - assert_eq!(node("/-node 1 2 3 key=\"value\" \\\n{\nnode\n}"), Ok(("", None))); + assert_eq!( + node("/-node 1 2 3 key=\"value\" \\\n{\nnode\n}"), + Ok(("", None)) + ); } #[test] fn test_arg_slashdash_comment() { - assert_eq!(node("node /-1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /-1 2"), Ok(("", Some(KdlNode { - name: "node".into(), - values: vec![KdlNodeValue::Int(2)], - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node 1 /- 2 3"), Ok(("", Some(KdlNode { - name: "node".into(), - values: vec![KdlNodeValue::Int(1), KdlNodeValue::Int(3)], - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /--1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /- -1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node \\\n/- -1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); + assert_eq!( + node("node /-1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /-1 2"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: vec![KdlValue::Int(2)], + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node 1 /- 2 3"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: vec![KdlValue::Int(1), KdlValue::Int(3)], + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /--1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /- -1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node \\\n/- -1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); } #[test] fn test_prop_slashdash_comment() { let mut properties = HashMap::new(); - properties.insert("key".to_owned(), KdlNodeValue::Int(1)); - assert_eq!(node("node /-key=1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /- key=1"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node key=1 /-key2=2"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties, - children: Vec::new(), - })))); + properties.insert("key".to_owned(), KdlValue::Int(1)); + assert_eq!( + node("node /-key=1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /- key=1"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node key=1 /-key2=2"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties, + children: Vec::new(), + }) + )) + ); } #[test] fn test_children_slashdash_comment() { - assert_eq!(node("node /-{}"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /- {}"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); - assert_eq!(node("node /-{\nnode2\n}"), Ok(("", Some(KdlNode { - name: "node".into(), - values: Vec::new(), - properties: HashMap::new(), - children: Vec::new(), - })))); + assert_eq!( + node("node /-{}"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /- {}"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); + assert_eq!( + node("node /-{\nnode2\n}"), + Ok(( + "", + Some(KdlNode { + name: "node".into(), + values: Vec::new(), + properties: HashMap::new(), + children: Vec::new(), + }) + )) + ); } #[test] @@ -583,8 +827,8 @@ mod tests { #[test] fn test_boolean() { - assert_eq!(boolean("true"), Ok(("", KdlNodeValue::Boolean(true)))); - assert_eq!(boolean("false"), Ok(("", KdlNodeValue::Boolean(false)))); + assert_eq!(boolean("true"), Ok(("", KdlValue::Boolean(true)))); + assert_eq!(boolean("false"), Ok(("", KdlValue::Boolean(false)))); assert!(boolean("blah").is_err()); }