use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
#[derive(Debug, Clone, Default, PartialEq)]
pub enum Patch<T> {
#[default]
Absent,
Null,
Value(T),
}
impl<T> Patch<T> {
#[must_use]
#[inline]
pub fn is_absent(&self) -> bool {
matches!(self, Patch::Absent)
}
}
impl<'de, T> Deserialize<'de> for Patch<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Option::<T>::deserialize(deserializer).map(|opt| match opt {
None => Patch::Null,
Some(value) => Patch::Value(value),
})
}
}
impl<T> Serialize for Patch<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Patch::Absent | Patch::Null => serializer.serialize_none(),
Patch::Value(value) => serializer.serialize_some(value),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Holder {
#[serde(default, skip_serializing_if = "Patch::is_absent")]
value: Patch<f64>,
}
#[test]
fn test_patch_deserialize_absent_key_is_absent() {
let holder: Holder = serde_json::from_str("{}").expect("empty object deserializes");
assert_eq!(holder.value, Patch::Absent);
}
#[test]
fn test_patch_deserialize_explicit_null_is_null() {
let holder: Holder =
serde_json::from_value(json!({ "value": null })).expect("null deserializes");
assert_eq!(holder.value, Patch::Null);
}
#[test]
fn test_patch_deserialize_value_is_value() {
let holder: Holder =
serde_json::from_value(json!({ "value": 1.5 })).expect("value deserializes");
assert_eq!(holder.value, Patch::Value(1.5));
}
#[test]
fn test_patch_serialize_absent_is_omitted() {
let holder = Holder {
value: Patch::Absent,
};
let json = serde_json::to_value(&holder).expect("serializes");
assert_eq!(json, json!({}));
}
#[test]
fn test_patch_serialize_null_is_null() {
let holder = Holder { value: Patch::Null };
let json = serde_json::to_value(&holder).expect("serializes");
assert_eq!(json, json!({ "value": null }));
}
#[test]
fn test_patch_serialize_value_is_value() {
let holder = Holder {
value: Patch::Value(2.5),
};
let json = serde_json::to_value(&holder).expect("serializes");
assert_eq!(json, json!({ "value": 2.5 }));
}
#[test]
fn test_patch_default_is_absent() {
assert_eq!(Patch::<u64>::default(), Patch::Absent);
assert!(Patch::<u64>::default().is_absent());
}
#[test]
fn test_patch_is_absent_only_for_absent() {
assert!(Patch::<u64>::Absent.is_absent());
assert!(!Patch::<u64>::Null.is_absent());
assert!(!Patch::Value(1u64).is_absent());
}
}