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
// ...
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!(),
}
}
}