//! Three-state container for PATCH fields.
//!
//! `serde`'s default behaviour collapses both "field missing" and
//! "field is `null`" to `Option::None`, which means an `Option<T>`
//! patch field can't distinguish "leave alone" from "set to NULL".
//! `Patch<T>` carries that distinction by deserializing JSON `null`
//! into `Clear` and any value into `Set`; with `#[serde(default)]`
//! on the field, a missing key falls through to `Unchanged`.

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum Patch<T> {
    /// Field absent from the request — leave the column untouched.
    #[default]
    Unchanged,
    /// Field present and explicitly `null` — set the column to NULL.
    Clear,
    /// Field present with a value — set the column to that value.
    Set(T),
}

impl<T> Patch<T> {
    /// Whether the request indicated this field should be written
    /// (either to a new value or to NULL).
    pub fn is_provided(&self) -> bool {
        !matches!(self, Patch::Unchanged)
    }

    /// The value to bind when writing, or `None` for `Unchanged`/`Clear`.
    pub fn set_value(&self) -> Option<&T> {
        match self {
            Patch::Set(v) => Some(v),
            _ => None,
        }
    }
}

impl<'de, T> serde::Deserialize<'de> for Patch<T>
where
    T: serde::Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Option::<T>::deserialize(deserializer).map(|opt| match opt {
            Some(v) => Patch::Set(v),
            None => Patch::Clear,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::Deserialize;
    use serde_json::json;

    #[derive(Deserialize)]
    struct Holder {
        #[serde(default)]
        desc: Patch<String>,
    }

    #[test]
    fn missing_key_is_unchanged() {
        let h: Holder = serde_json::from_value(json!({})).unwrap();
        assert_eq!(h.desc, Patch::Unchanged);
    }

    #[test]
    fn explicit_null_is_clear() {
        let h: Holder = serde_json::from_value(json!({ "desc": null })).unwrap();
        assert_eq!(h.desc, Patch::Clear);
    }

    #[test]
    fn value_is_set() {
        let h: Holder = serde_json::from_value(json!({ "desc": "x" })).unwrap();
        assert_eq!(h.desc, Patch::Set("x".into()));
    }
}
