Skip to content

Instantly share code, notes, and snippets.

@ngerakines
Last active November 11, 2024 15:20
Show Gist options
  • Save ngerakines/3493cf363db01eb714fdd1938264f4e4 to your computer and use it in GitHub Desktop.
Save ngerakines/3493cf363db01eb714fdd1938264f4e4 to your computer and use it in GitHub Desktop.
Rust plaground code that parses json value as string or complex enum
use std::str::FromStr;
use std::fmt;
use std::marker::PhantomData;
use anyhow::anyhow;
use serde::{Deserialize, Serialize, Deserializer};
use serde::de::{self, Visitor, MapAccess};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum FeedQuery {
#[serde(rename="simple")]
Simple{},
#[serde(rename="popular")]
Popular {
#[serde(default)]
age_floor: i64,
}
}
impl Default for FeedQuery {
fn default() -> Self { FeedQuery::Simple{} }
}
impl FromStr for FeedQuery {
type Err = anyhow::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"simple" => Ok(FeedQuery::Simple{}),
_ => Err(anyhow!("unsupported query"))
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Feed {
pub name: String,
#[serde(default, deserialize_with = "string_or_struct")]
pub query: FeedQuery,
}
fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de> + FromStr<Err = anyhow::Error>,
D: Deserializer<'de>,
{
struct StringOrStruct<T>(PhantomData<fn() -> T>);
impl<'de, T> Visitor<'de> for StringOrStruct<T>
where
T: Deserialize<'de> + FromStr<Err = anyhow::Error>,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or FeedQuery")
}
fn visit_str<E>(self, value: &str) -> Result<T, E>
where
E: de::Error,
{
FromStr::from_str(value).map_err(|_| de::Error::custom("cannot deserialize field"))
}
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
where
M: MapAccess<'de>,
{
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
}
}
deserializer.deserialize_any(StringOrStruct(PhantomData))
}
fn main() {
let json = r#"
{
"name": "popular",
"query": "simple"
}
"#;
let feed = serde_json::from_str::<Feed>(json);
println!("{:?}", feed);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment