use super::configuration;
use serde::de;
use std::fmt;

impl<'a> serde::Deserialize<'a> for configuration::Path {
    fn deserialize<T>(deserializer: T) -> Result<configuration::Path, T::Error>
    where
        T: serde::Deserializer<'a>,
    {
        deserializer.deserialize_str(Visitor)
    }
}

struct Visitor;

impl de::Visitor<'_> for Visitor {
    type Value = configuration::Path;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a path (like `x` or `x::y::z`)")
    }

    fn visit_str<T>(self, string: &str) -> Result<Self::Value, T>
    where
        T: de::Error,
    {
        match syn::parse_str(string) {
            Err(_) => Err(de::Error::invalid_value(de::Unexpected::Str(string), &self)),
            Ok(value) => Ok(configuration::Path(value)),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[cfg(test)]
    mod deserialize {
        use super::*;
        use std::cmp;

        #[derive(cmp::PartialEq, Debug, serde::Deserialize)]
        struct Binding {
            name: configuration::Path,
        }

        #[test]
        fn handles_one_segment() {
            let actual: Result<Binding, _> = toml::from_str("name = 'my_value'");

            let actual = actual.unwrap();
            let expected = Binding {
                name: configuration::Path(syn::parse_str("my_value").unwrap()),
            };
            assert_eq!(actual, expected);
        }

        #[test]
        fn handles_multiple_segments() {
            let actual: Result<Binding, _> = toml::from_str("name = 'a::b::c'");

            let actual = actual.unwrap();
            let expected = Binding {
                name: configuration::Path(syn::parse_str("a::b::c").unwrap()),
            };
            assert_eq!(actual, expected);
        }

        #[test]
        fn given_invalid_value_it_errs() {
            let actual: Result<Binding, _> = toml::from_str("name = 'a bc'");

            let actual = actual.is_err();
            assert!(actual);
        }
    }
}
