-
-
Notifications
You must be signed in to change notification settings - Fork 80
Description
Consider a generic metadata struct:
/// Whether this value has been manually approved
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(bound(deserialize = "T: Deserialize<'de>"))]
struct Metadata<T> {
/// Current, validated value of this field
///
/// `None` if validation failed
pub value: Option<T>,
/// Value before changes were made during approval
///
/// `None` if no changes were made
#[serde(skip_serializing_if = "Option::is_none")]
pub original_value: Option<T>,
/// Whether this field has been manually vetted
has_been_vetted: bool,
}
For basic use cases, this is fine. However, we need to implement SerializeAs
and DeserializeAs
in order to support modifications to the interior. The desired use case looks like
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize)]
struct User {
username: Metadata<String>,
// `date_as_string::Ymd` is working fine, so we don't demo the implementation here
#[serde_as(as = "Option<Metadata<date_as_string::Ymd>>")]
birthday: Option<Metadata<time::Date>>,
}
For that use case, we need implementations for SerializeAs
and DeserializeAs
:
impl<T, U> SerializeAs<Metadata<T>> for Metadata<U>
where
U: SerializeAs<T>,
{
fn serialize_as<S>(source: &Metadata<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
todo!()
}
}
impl<'de, T, U> DeserializeAs<'de, Metadata<T>> for Metadata<U>
where
U: DeserializeAs<'de, T>,
{
fn deserialize_as<D>(deserializer: D) -> Result<Metadata<T>, D::Error>
where
D: Deserializer<'de>,
{
todo!()
}
}
Unfortunately, the documentation is very unclear as to how precisely to accomplish this, and I'm finding myself confused about how to proceed. At this point, it seems as though the best process might actually be to cargo expand
the Serialize
and Deserialize
implementations, and then adjust wherever generic values are present in order to wrap them with SerializeAsWrap
or DeserializeAsWrap
wherever a generic parameter exists, essentially reimplementing precisely the same logic that serde
already does for field naming, optional fields, etc. But that is extremely cumbersome, so hopefully there is a better way!