Skip to content
Clone
d13fe56
47 lines 1.5 KB Rust
use serde::Deserialize;
use serde::de::{self, Deserializer, Visitor};
use std::fmt;
use std::marker::PhantomData;
/// Deserialize the three-state update field `Option<Option<T>>`.
///
/// The documented PATCH contract (`lib/webapi/src/openapi.rs`) is:
/// field absent → unchanged, field `null` → cleared, field value → set.
///
/// serde does not unwrap the outer `Option` by itself, so a derived
/// `Option<Option<T>>` collapses "absent" and "null" into `None` and every
/// clear request silently no-ops. This maps `null` to `Some(None)` and a value
/// to `Some(Some(value))`, which is what the service layer branches on.
pub fn deserialize_opt_opt<'de, D, T>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
deserializer.deserialize_option(OptOptVisitor(PhantomData))
}
struct OptOptVisitor<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for OptOptVisitor<T>
where
T: Deserialize<'de>,
{
type Value = Option<Option<T>>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a value, null, or nothing")
}
/// JSON `null`: the caller asked for the field to be cleared.
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Some(None))
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Some(None))
}
fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
T::deserialize(deserializer).map(|value| Some(Some(value)))
}
}