How can I support an unknown or other value for a Serde enum?

前端 未结 3 671
野的像风
野的像风 2021-01-14 01:09

I have a JSON API that returns an object that looks like this:

{
  \"PrivatePort\": 2222,
  \"PublicPort\": 3333,
  \"Type\": \"tcp\"
}

To

3条回答
  •  隐瞒了意图╮
    2021-01-14 01:23

    Simple case should be fine with this:

    use serde::de::Visitor;
    use serde::{Deserialize, Deserializer, Serialize};
    use serde_json::from_str;
    
    #[derive(Deserialize, Serialize, Debug)]
    #[serde(rename_all = "PascalCase")]
    pub struct PortMapping {
        pub private_port: u16,
        pub public_port: u16,
        #[serde(rename = "Type")]
        pub port_type: PortType,
    }
    
    #[derive(Clone, Eq, PartialEq, Serialize, Debug)]
    pub enum PortType {
        Sctp,
        Tcp,
        Udp,
        Unknown(String),
    }
    
    const PORT_TYPE: &'static [(&'static str, PortType)] = &[
        ("sctp", PortType::Sctp),
        ("tcp", PortType::Tcp),
        ("udp", PortType::Udp),
    ];
    
    impl From for PortType {
        fn from(variant: String) -> Self {
            PORT_TYPE
                .iter()
                .find(|(id, _)| *id == &*variant)
                .map(|(_, port_type)| port_type.clone())
                .unwrap_or(PortType::Unknown(variant))
        }
    }
    
    impl<'a> From<&'a str> for PortType {
        fn from(variant: &'a str) -> Self {
            PORT_TYPE
                .iter()
                .find(|(id, _)| *id == &*variant)
                .map(|(_, port_type)| port_type.clone())
                .unwrap_or_else(|| PortType::Unknown(variant.to_string()))
        }
    }
    
    impl<'de> Deserialize<'de> for PortType {
        fn deserialize(de: D) -> Result
        where
            D: Deserializer<'de>,
        {
            struct PortTypeVisitor {}
    
            impl<'de> Visitor<'de> for PortTypeVisitor {
                type Value = PortType;
    
                fn expecting(
                    &self,
                    fmt: &mut std::fmt::Formatter<'_>,
                ) -> std::result::Result<(), std::fmt::Error> {
                    fmt.write_str("We expected a string")
                }
    
                fn visit_str(self, variant: &str) -> Result {
                    Ok(variant.into())
                }
    
                fn visit_string(self, variant: String) -> Result {
                    Ok(variant.into())
                }
            }
    
            de.deserialize_string(PortTypeVisitor {})
        }
    }
    
    fn main() {
        let input = r#"
        {
          "PrivatePort": 2222,
          "PublicPort": 3333,
          "Type": "dccp"
        }
        "#;
    
        let result: Result = from_str(input);
    
        println!("{:#?}", result);
    }
    

    I don't think there is a idiomatic way to do this, that could be included in the future.

提交回复
热议问题