//! 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` //! patch field can't distinguish "leave alone" from "set to NULL". //! `Patch` 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 { /// 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 Patch { /// 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 where T: serde::Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { Option::::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, } #[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())); } }