use serde::{Deserialize, Deserializer};

pub fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
    T: Default + Deserialize<'de>,
    D: Deserializer<'de>,
{
    Option::deserialize(deserializer).map(Option::unwrap_or_default)
}

#[cfg(test)]
mod tests {
    use serde::Deserialize;

    #[derive(Debug, Deserialize, PartialEq, Eq)]
    struct TestData {
        #[serde(deserialize_with = "crate::deserialize_null_as_default")]
        pub string: String,

        #[serde(deserialize_with = "crate::deserialize_null_as_default")]
        pub number: i64,

        #[serde(deserialize_with = "crate::deserialize_null_as_default")]
        pub boolean: bool,

        #[serde(deserialize_with = "crate::deserialize_null_as_default")]
        pub list: Vec<String>,
    }

    #[test]
    pub fn deserialize_null_as_default_deserializes_value() {
        let input = r#"{"string":"data","number":42,"boolean":true,"list":["a","b","c"]}"#;

        let value = serde_json::from_str::<TestData>(input).unwrap();

        assert_eq!(
            value,
            TestData {
                string: "data".into(),
                number: 42,
                boolean: true,
                list: vec!["a".into(), "b".into(), "c".into()]
            }
        )
    }

    #[test]
    pub fn deserialize_null_as_default_deserializes_null_as_default() {
        let input = r#"{"string":null,"number":null,"boolean":null,"list":null}"#;

        let value = serde_json::from_str::<TestData>(input).unwrap();

        assert_eq!(
            value,
            TestData {
                string: "".into(),
                number: 0,
                boolean: false,
                list: vec![]
            }
        )
    }
}
