use serde::Deserialize;
pub(crate) fn de_present<'de, D, T>(d: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
{
T::deserialize(d).map(Some)
}
pub(crate) fn de_present_object<'de, D>(d: D) -> Result<Option<serde_json::Value>, D::Error>
where
D: serde::Deserializer<'de>,
{
let v = serde_json::Value::deserialize(d)?;
if !v.is_object() {
let kind = match &v {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(_) => "number",
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => unreachable!("is_object() was false"),
};
return Err(serde::de::Error::custom(format!(
"field present but {kind}; the ACDP schema types it as a \
non-nullable JSON object"
)));
}
Ok(Some(v))
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
use serde_json::json;
#[derive(Debug, Deserialize)]
struct Holder {
#[serde(default, deserialize_with = "de_present")]
bare: Option<String>,
#[serde(default, deserialize_with = "de_present_object")]
obj: Option<serde_json::Value>,
}
#[test]
fn de_present_absent_key_yields_none() {
let h: Holder = serde_json::from_value(json!({})).unwrap();
assert_eq!(h.bare, None);
assert_eq!(h.obj, None);
}
#[test]
fn de_present_accepts_real_value() {
let h: Holder = serde_json::from_value(json!({"bare": "x"})).unwrap();
assert_eq!(h.bare.as_deref(), Some("x"));
}
#[test]
fn de_present_rejects_explicit_null() {
let err = serde_json::from_value::<Holder>(json!({"bare": null})).unwrap_err();
assert!(
err.to_string().contains("null"),
"expected an 'invalid type: null' message, got: {err}"
);
}
#[test]
fn de_present_object_accepts_object() {
let h: Holder = serde_json::from_value(json!({"obj": {"k": 1}})).unwrap();
assert_eq!(h.obj, Some(json!({"k": 1})));
}
#[test]
fn de_present_object_rejects_null_and_non_objects() {
for (val, kind) in [
(json!(null), "null"),
(json!(true), "boolean"),
(json!(7), "number"),
(json!("s"), "string"),
(json!([1, 2]), "array"),
] {
let err = serde_json::from_value::<Holder>(json!({"obj": val})).unwrap_err();
assert!(
err.to_string().contains(kind),
"obj={val} should be rejected naming {kind}, got: {err}"
);
}
}
}