use serde_json::Value;

/// rkyv adapter for `Option<serde_json::Value>` that archives as an `Option<String>`.
pub struct JsonValueOptionAsStringOption;

impl rkyv::with::ArchiveWith<Option<Value>> for JsonValueOptionAsStringOption {
    type Archived = <Option<String> as rkyv::Archive>::Archived;
    type Resolver = <Option<String> as rkyv::Archive>::Resolver;

    fn resolve_with(
        field: &Option<Value>,
        resolver: Self::Resolver,
        out: rkyv::place::Place<Self::Archived>,
    ) {
        let json = field.as_ref().map(|value| {
            serde_json::to_string(value).expect("metadata must be serializable to JSON")
        });
        <Option<String> as rkyv::Archive>::resolve(&json, resolver, out);
    }
}

impl<S: rkyv::rancor::Fallible + ?Sized> rkyv::with::SerializeWith<Option<Value>, S>
    for JsonValueOptionAsStringOption
where
    Option<String>: rkyv::Serialize<S>,
{
    fn serialize_with(
        field: &Option<Value>,
        serializer: &mut S,
    ) -> Result<Self::Resolver, S::Error> {
        let json = field.as_ref().map(|value| {
            serde_json::to_string(value).expect("metadata must be serializable to JSON")
        });
        <Option<String> as rkyv::Serialize<S>>::serialize(&json, serializer)
    }
}

impl<D: rkyv::rancor::Fallible + ?Sized>
    rkyv::with::DeserializeWith<<Option<String> as rkyv::Archive>::Archived, Option<Value>, D>
    for JsonValueOptionAsStringOption
{
    fn deserialize_with(
        field: &<Option<String> as rkyv::Archive>::Archived,
        _: &mut D,
    ) -> Result<Option<Value>, D::Error> {
        use rkyv::option::ArchivedOption;

        match field {
            ArchivedOption::None => Ok(None),
            ArchivedOption::Some(json) => Ok(Some(
                serde_json::from_str(json.as_str()).expect("archived metadata must be valid JSON"),
            )),
        }
    }
}
