confiner 0.2.1

A config language for things that look like trees.
Documentation
use super::*;

use serde::Deserializer;

impl<'de> serde::de::IntoDeserializer<'de> for &grammar::Expr
{
    type Deserializer = Self;

    fn into_deserializer(self) -> Self { self }
}

impl<'de> serde::Deserializer<'de> for &grammar::Expr
{
    type Error = serde::de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where V: serde::de::Visitor<'de>
    {
        match self
        {
            grammar::Expr::Null       => visitor.visit_none(),
            grammar::Expr::Bool(b)    => visitor.visit_bool(*b),
            grammar::Expr::Int(i)     => visitor.visit_i64(*i),
            grammar::Expr::Float(f)   => visitor.visit_f64(*f),
            grammar::Expr::Str(s)     => visitor.visit_string(s.clone()),
            grammar::Expr::List(list) =>
                serde::de::value::SeqDeserializer::new(list.iter())
                    .deserialize_any(visitor),
            grammar::Expr::Map(map) =>
                serde::de::value::MapDeserializer::new(
                    map.into_iter().map(|(k, v)| (k.as_str(), v))
                )
                    .deserialize_any(visitor),
            grammar::Expr::Enum(e) =>
                serde::de::value::EnumAccessDeserializer::new(e)
                    .deserialize_any(visitor),
            grammar::Expr::Path(p) =>
                visitor.visit_bytes(p.as_os_str().as_encoded_bytes())
        }
    }

    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where V: serde::de::Visitor<'de>
    {
        if matches!(self, grammar::Expr::Null)
        {
            visitor.visit_none()
        }
        else
        {
            visitor.visit_some(self)
        }
    }

    serde::forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }
}

impl<'de> serde::Deserializer<'de> for &grammar::BlockInfo
{
    type Error = serde::de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
        where V: serde::de::Visitor<'de>
    {
        serde::de::value::MapDeserializer::new(
            self.properties.iter().map(|(k, v)| (k.as_str(), v))
        )
            .deserialize_any(visitor)
    }

    serde::forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

}

impl<'de> serde::de::EnumAccess<'de> for &grammar::Enum
{
    type Error = serde::de::value::Error;
    type Variant = Self;

    fn variant_seed<V>(self, seed: V)
        -> Result<(V::Value, Self::Variant), Self::Error>
        where V: serde::de::DeserializeSeed<'de>
    {
        let name = seed.deserialize(
            serde::de::value::StrDeserializer::new(&self.name)
        )?;

        Ok((name, self))
    }
}

impl<'de> serde::de::VariantAccess<'de> for &grammar::Enum
{
    type Error = serde::de::value::Error;

    fn unit_variant(self) -> Result<(), Self::Error>
    {
        match &self.value
        {
            None    => Ok(()),
            Some(_) => Err(serde::de::Error::invalid_type(
                serde::de::Unexpected::NewtypeVariant, &"unit variant"
            ))
        }
    }

    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
        where T: serde::de::DeserializeSeed<'de>
    {
        match &self.value
        {
            None => Err(serde::de::Error::invalid_type(
                serde::de::Unexpected::UnitVariant, &"newtype variant"
            )),
            Some(data) => seed.deserialize(data.as_ref())
        }
    }

    fn tuple_variant<V>(self, _len: usize, visitor: V)
        -> Result<V::Value, Self::Error>
        where V: serde::de::Visitor<'de>
    {
        match &self.value
        {
            None => Err(serde::de::Error::invalid_type(
                serde::de::Unexpected::UnitVariant, &"tuple variant"
            )),
            Some(data) => data.deserialize_any(visitor)
        }
    }

    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V)
        -> Result<V::Value, Self::Error>
        where V: serde::de::Visitor<'de>
    {
        match &self.value
        {
            None => Err(serde::de::Error::invalid_type(
                serde::de::Unexpected::UnitVariant, &"struct variant"
            )),
            Some(data) => data.deserialize_any(visitor)
        }
    }
}

#[cfg(test)]
fn assert_deserializes_to<T>(s: &str, exp: T)
    where T: for<'de> serde::Deserialize<'de> + std::fmt::Debug + PartialEq
{
    let expr = grammar::expr(s, "/".as_ref()).unwrap();

    use serde::Deserialize;
    assert_eq!(T::deserialize(&expr).unwrap(), exp);
}

#[test]
fn test_deser_structure()
{
    #[derive(serde::Deserialize, Debug, PartialEq)]
    struct ToDeser
    {
        b: bool,
        i: i32,
        f: f32,
        s: String
    }

    assert_deserializes_to(
        r#"{ b = true, i = 1, f = 2.0, s = "Hello" }"#,
        ToDeser { b: true, i: 1, f: 2.0, s: "Hello".to_owned() }
    );
}

#[test]
fn test_deser_block_to_structure()
{
    #[derive(serde::Deserialize, Debug, PartialEq)]
    struct ToDeser
    {
        b: bool,
        i: i32,
        f: f32,
        s: String
    }

    let block = grammar::block(
        r#": block { b = true, i = 1, f = 2.0, s = "Hello" }"#,
        "/".as_ref()
    )
        .unwrap();

    use serde::Deserialize;
    assert_eq!(
        ToDeser::deserialize(block.as_ref()).unwrap(),
        ToDeser { b: true, i: 1, f: 2.0, s: "Hello".to_owned() }
    );
}

#[test]
fn test_deser_enum()
{
    #[derive(serde::Deserialize, Debug, PartialEq)]
    enum ToDeser
    {
        Unit,
        NewType(bool),
        Tuple(i32, i32),
        Struct { s: String }
    }

    assert_deserializes_to(r#"!Unit"#, ToDeser::Unit);
    assert_deserializes_to(r#"!NewType true"#, ToDeser::NewType(true));
    assert_deserializes_to(r#"!Tuple [1, 2]"#, ToDeser::Tuple(1, 2));
    assert_deserializes_to(
        r#"!Struct { s = "Hello" }"#,
        ToDeser::Struct { s: "Hello".to_owned() }
    );
}

#[test]
fn test_deser_list()
{
    assert_deserializes_to(r#"[1, 2, 3]"#, vec![1, 2, 3]);
}

#[test]
fn test_deser_map()
{
    let map: HashMap<String, String> =
        [("k1", "v1"), ("k2", "v2")].into_iter()
            .map(|(k, v)| (k.to_owned(), v.to_owned()))
            .collect();

    assert_deserializes_to(r#"{ k1 = "v1", k2 = "v2" }"#, map);
}

#[test]
fn test_deser_path()
{
    use std::path::PathBuf;
    use serde::Deserialize;

    assert_eq!(
        PathBuf::deserialize(
            &grammar::expr("./test.txt", "/".as_ref()).unwrap()
        )
            .unwrap(),
        PathBuf::from("/test.txt")
    );

    assert_eq!(
        PathBuf::deserialize(
            &grammar::expr("./test.txt", "/root".as_ref()).unwrap()
        )
            .unwrap(),
        PathBuf::from("/root/test.txt")
    );
}

#[test]
fn test_deser_option()
{
    #[derive(serde::Deserialize, Debug, PartialEq)]
    struct Test { opt: Option<String> }

    assert_deserializes_to(
        r#"{ opt = "MEOW" }"#,
        Test { opt: Some(String::from("MEOW")) }
    );
}