Search code examples
rustserializationdeserializationserde

How to re-use existing, derived deserialize implementation when deserializing some variants of an enum?


I have an enum that contains variants which implement ser/de via derive, and ones which do not. I would like to implement Deserialize in such a way that I do not have to match every possible sub-variant, e.g. re-use the existing, derived Deserialize implementation.

Here's the example:

enum GlobalError {
    MyErrorVariant(MyError), // Implements ser/de (derived bellow)
    AnyhowError(anyhow::Error) // Anyhow, does not impl ser/de
}

#[derive(Serialize, Deserialize)]
enum MyError {
    BadData,
    // Many many variants
}

impl<'de> serde::Deserialize<'de> for GlobalError {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de> {
            deserializer.deserialize_any(GlobalErrorVisitor)
    }
}

struct GlobalErrorVisitor;
impl<'de> Visitor<'de> for GlobalErrorVisitor{
    type Value = GlobalError;

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        let Some((key, value)) = map.next_entry::<String, String>()? else { unreachable!("Error deserialzied empty map?") };

        match key.as_ref() {
            "AnyhowError" => Ok(GlobalError::AnyhowError(anyhow::anyhow!(value))),
            "MyError" => {
                match value.as_ref() {
                    // I do not want to str match for every single MyError variant.
                    // I would like to re-use MyError's derived implementation of Deserialize
                    // However, I don't have accesses to a deserializer here,
                    // and I don't have accesses to self in impl Deserialize?
                    "BadData" => Ok(GlobalError::MyErrorVariant(MyError::BadData)),
                    _ => todo!(),
                }
            },
            _ => todo!(),
        }
    }
}

// ...
// impl Serialize with serialize_newtype_variant for both, the special case serde value being to_stringed
// ...


Solution

  • You are parsing the map entries together as (String, String), which means you have baked in an assumption that all values have the same type:

    let Some((key, value)) = map.next_entry::<String, String>()?
    

    Instead, parse each key first, and then parse the values into the types you expect for that key:

    struct GlobalErrorVisitor;
    
    impl<'de> Visitor<'de> for GlobalErrorVisitor {
        type Value = GlobalError;
    
        fn expecting(&self, _: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
            todo!()
        }
    
        fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
        where
            A: serde::de::MapAccess<'de>,
        {
            let Some(key) = map.next_key::<String>()? else { unreachable!("Error deserialzied empty map?") };
    
            match key.as_ref() {
                "AnyhowError" => {
                    let err: String = map.next_value()?;
                    Ok(GlobalError::AnyhowError(anyhow::anyhow!(err)))
                }
                "MyError" => {
                    let err = map.next_value()?;
                    Ok(GlobalError::MyErrorVariant(err))
                }
                _ => todo!(),
            }
        }
    }