I added an enum for my webscraper to deserialize data from a JSON field that represents an HTML image size, which can either be an unsigned int like 1080 or a string like "100%":
use serde::Deserialize;
use std::fmt::{Display, Formatter};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HtmlSize {
String(String),
Int(u64),
}
impl Display for HtmlSize {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(string) => write!(f, r#""{}""#, string),
Self::Int(int) => write!(f, "{}", int),
}
}
}
impl<'de> Deserialize<'de> for HtmlSize {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(HtmlSizeVisitor)
}
}
struct HtmlSizeVisitor;
impl<'de> serde::de::Visitor<'de> for HtmlSizeVisitor {
type Value = HtmlSize;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
write!(
formatter,
"an unsigned integer or a string representing a width"
)
}
fn visit_u64<E: serde::de::Error>(self, n: u64) -> Result<HtmlSize, E> {
Ok(HtmlSize::Int(n))
}
fn visit_str<E: serde::de::Error>(self, string: &str) -> Result<HtmlSize, E> {
Ok(HtmlSize::String(string.to_string()))
}
}
Example usage:
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct ImageInfo {
id: String,
alt: String,
height: HtmlSize,
src: String,
width: HtmlSize,
caption: String,
credit: String,
}
I did not find anything readily available like this, so I implemented the above HtmlSize with its corresponding visitor. Is this a sensible implementation or did I reinvent the wheel? Can the implementation be improved?