Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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
// ...

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>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!(),
        }
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading