I have a JSON API that returns an object that looks like this:
{
\"PrivatePort\": 2222,
\"PublicPort\": 3333,
\"Type\": \"tcp\"
}
To
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.