diff options
Diffstat (limited to 'src/util/template.rs')
-rw-r--r-- | src/util/template.rs | 362 |
1 files changed, 0 insertions, 362 deletions
diff --git a/src/util/template.rs b/src/util/template.rs deleted file mode 100644 index bb77f9b4d610..000000000000 --- a/src/util/template.rs +++ /dev/null @@ -1,362 +0,0 @@ -use nom::combinator::rest; -use nom::error::ErrorKind; -use nom::{Err, IResult}; -use std::collections::HashMap; -use std::fmt::{self, Display}; -use std::marker::PhantomData; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Path<'a> { - head: &'a str, - tail: Vec<&'a str>, -} - -impl<'a> Path<'a> { - fn new(head: &'a str, tail: Vec<&'a str>) -> Self { - Path { head, tail } - } -} - -impl<'a> Display for Path<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.head)?; - for part in &self.tail { - write!(f, ".{}", part)?; - } - Ok(()) - } -} - -// named!(path_ident, map_res!(is_not!(".}"), std::str::from_utf8)); -fn path_ident<'a>(input: &'a str) -> IResult<&'a str, &'a str> { - take_till!(input, |c| c == '.' || c == '}') -} - -fn path<'a>(input: &'a str) -> IResult<&'a str, Path<'a>> { - map!( - input, - tuple!( - path_ident, - many0!(complete!(preceded!(char!('.'), path_ident))) - ), - |(h, t)| Path::new(h, t) - ) -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum TemplateToken<'a> { - Literal(&'a str), - Substitution(Path<'a>), -} - -fn token_substitution<'a>( - input: &'a str, -) -> IResult<&'a str, TemplateToken<'a>> { - map!( - input, - delimited!(tag!("{{"), path, tag!("}}")), - TemplateToken::Substitution - ) -} - -fn template_token<'a>(input: &'a str) -> IResult<&'a str, TemplateToken<'a>> { - alt!( - input, - token_substitution - | map!( - alt!(complete!(take_until!("{{")) | complete!(rest)), - TemplateToken::Literal - ) - ) -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Template<'a> { - tokens: Vec<TemplateToken<'a>>, -} - -impl<'a> Template<'a> { - pub fn new(tokens: Vec<TemplateToken<'a>>) -> Self { - Template { tokens } - } -} - -pub struct TemplateVisitor<'a> { - marker: PhantomData<fn() -> Template<'a>>, -} - -impl<'a> TemplateVisitor<'a> { - pub fn new() -> Self { - TemplateVisitor { - marker: PhantomData, - } - } -} - -impl<'a> serde::de::Visitor<'a> for TemplateVisitor<'a> { - type Value = Template<'a>; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("a valid template string") - } - - fn visit_borrowed_str<E: serde::de::Error>( - self, - v: &'a str, - ) -> Result<Self::Value, E> { - Template::parse(v).map_err(|_| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(v), - &"a valid template string", - ) - }) - } -} - -impl<'a> serde::Deserialize<'a> for Template<'a> { - fn deserialize<D: serde::Deserializer<'a>>( - deserializer: D, - ) -> Result<Self, D::Error> { - deserializer.deserialize_str(TemplateVisitor::new()) - } -} - -impl<'a> Template<'a> { - pub fn parse( - input: &'a str, - ) -> Result<Template<'a>, Err<(&'a str, ErrorKind)>> { - let (remaining, res) = template(input)?; - if !remaining.is_empty() { - unreachable!(); - } - Ok(res) - } - - pub fn format( - &self, - params: &TemplateParams<'a>, - ) -> Result<String, TemplateError<'a>> { - use TemplateToken::*; - let mut res = String::new(); - for token in &self.tokens { - match token { - Literal(s) => res.push_str(s), - Substitution(p) => match params.get(p.clone()) { - Some(s) => res.push_str(s), - None => return Err(TemplateError::MissingParam(p.clone())), - }, - } - } - Ok(res) - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum TemplateError<'a> { - MissingParam(Path<'a>), -} - -impl<'a> Display for TemplateError<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TemplateError::*; - match self { - MissingParam(path) => { - write!(f, "Missing template parameter: {}", path) - } - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub enum TemplateParams<'a> { - Direct(&'a str), - Nested(HashMap<&'a str, TemplateParams<'a>>), -} - -impl<'a> TemplateParams<'a> { - fn get(&self, path: Path<'a>) -> Option<&'a str> { - use TemplateParams::*; - match self { - Direct(_) => None, - Nested(m) => m.get(path.head).and_then(|next| { - if path.tail.is_empty() { - match next { - Direct(s) => Some(*s), - _ => None, - } - } else { - next.get(Path { - head: path.tail[0], - tail: path.tail[1..].to_vec(), - }) - } - }), - } - } -} - -#[macro_export] -macro_rules! template_params { - (@count $head: expr => $hv: tt, $($rest:tt)+) => { 1 + template_params!(@count $($rest)+) }; - (@count $one:expr => $($ov: tt)*) => { 1 }; - (@inner $ret: ident, ($key: expr => {$($v:tt)*}, $($r:tt)*)) => { - $ret.insert($key, template_params!({ $($v)* })); - template_params!(@inner $ret, ($($r)*)); - }; - (@inner $ret: ident, ($key: expr => $value: expr, $($r:tt)*)) => { - $ret.insert($key, template_params!($value)); - template_params!(@inner $ret, ($($r)*)); - }; - (@inner $ret: ident, ()) => {}; - - ({ $($body: tt)* }) => {{ - let _cap = template_params!(@count $($body)*); - let mut _m = ::std::collections::HashMap::with_capacity(_cap); - template_params!(@inner _m, ($($body)*)); - TemplateParams::Nested(_m) - }}; - - ($direct:expr) => { TemplateParams::Direct($direct) }; - - () => { TemplateParams::Nested(::std::collections::HashMap::new()) }; -} - -fn template<'a>(input: &'a str) -> IResult<&'a str, Template<'a>> { - complete!( - input, - map!(many1!(complete!(template_token)), Template::new) - ) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_path_ident() { - assert_eq!(path_ident("foo}}"), Ok(("}}", "foo"))); - assert_eq!(path_ident("foo.bar}}"), Ok((".bar}}", "foo"))); - } - - #[test] - fn test_parse_path() { - assert_eq!(path("foo}}"), Ok(("}}", Path::new("foo", vec![])))); - assert_eq!( - path("foo.bar}}"), - Ok(("}}", Path::new("foo", vec!["bar"]))) - ); - assert_eq!( - path("foo.bar.baz}}"), - Ok(("}}", Path::new("foo", vec!["bar", "baz"]))) - ); - } - - #[test] - fn test_parse_template_token() { - assert_eq!( - template_token("foo bar"), - Ok(("", TemplateToken::Literal("foo bar"))) - ); - - assert_eq!( - template_token("foo bar {{baz}}"), - Ok(("{{baz}}", TemplateToken::Literal("foo bar "))) - ); - - assert_eq!( - template_token("{{baz}}"), - Ok(( - "", - TemplateToken::Substitution(Path::new("baz", Vec::new())) - )) - ); - - assert_eq!( - template_token("{{baz}} foo bar"), - Ok(( - " foo bar", - TemplateToken::Substitution(Path::new("baz", Vec::new())) - )) - ); - } - - #[test] - fn test_parse_template() { - assert_eq!( - template("foo bar"), - Ok(( - "", - Template { - tokens: vec![TemplateToken::Literal("foo bar")] - } - )) - ); - - assert_eq!( - template("foo bar {{baz}} qux"), - Ok(( - "", - Template { - tokens: vec![ - TemplateToken::Literal("foo bar "), - TemplateToken::Substitution(Path::new( - "baz", - Vec::new() - )), - TemplateToken::Literal(" qux"), - ] - } - )) - ); - } - - #[test] - fn test_template_params_literal() { - // trace_macros!(true); - let expected = template_params!({ - "direct" => "hi", - "other" => "here", - "nested" => { - "one" => "1", - "two" => "2", - "double" => { - "three" => "3", - }, - }, - }); - // trace_macros!(false); - assert_eq!( - TemplateParams::Nested(hashmap! { - "direct" => TemplateParams::Direct("hi"), - "other" => TemplateParams::Direct("here"), - "nested" => TemplateParams::Nested(hashmap!{ - "one" => TemplateParams::Direct("1"), - "two" => TemplateParams::Direct("2"), - "double" => TemplateParams::Nested(hashmap!{ - "three" => TemplateParams::Direct("3"), - }) - }) - }), - expected, - ) - } - - #[test] - fn test_format_template() { - assert_eq!( - "foo bar baz qux", - Template::parse("foo {{x}} {{y.z}} {{y.w.z}}") - .unwrap() - .format(&template_params!({ - "x" => "bar", - "y" => { - "z" => "baz", - "w" => { - "z" => "qux", - }, - }, - })) - .unwrap() - ) - } -} |