Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use std::fmt;
- use serde::{Deserialize, Deserializer}; // 1.0.91
- use serde::de::{self, Visitor, MapAccess};
- #[derive(Debug, Deserialize)]
- pub struct SingleUnitParam {
- name: String,
- units: String,
- }
- #[derive(Debug, Deserialize)]
- pub struct UnitInfo {
- units: String,
- }
- #[derive(Debug, Deserialize)]
- pub struct MultiUnits {
- metric: UnitInfo,
- imperial: UnitInfo,
- }
- #[derive(Debug, Deserialize)]
- #[serde(untagged)]
- enum StrOrUnitsObj<'a> {
- Str(&'a str),
- UnitsObj(MultiUnits)
- }
- #[derive(Debug, Deserialize)]
- pub struct MultiUnitParam {
- name: String,
- units: MultiUnits,
- }
- #[derive(Debug)]
- pub enum Param {
- Single(SingleUnitParam),
- Multiple(MultiUnitParam),
- }
- impl<'de> Deserialize<'de> for Param {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- enum Field { Name, UnitsAsObj, UnitsAsStr };
- impl<'de> Deserialize<'de> for Field {
- fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
- where
- D: Deserializer<'de>,
- {
- struct FieldVisitor;
- impl<'de> Visitor<'de> for FieldVisitor {
- type Value = Field;
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("`Name` or `Units`")
- }
- fn visit_str<E>(self, value: &str) -> Result<Field, E>
- where
- E: de::Error,
- {
- match value {
- "Name" => Ok(Field::Name),
- "Units" => Ok({
- let val = StrOrUnitsObj::deserialize(deserializer).unwrap();
- match val {
- StrOrUnitsObj::Str(s) => {
- Field::UnitsAsObj
- },
- StrOrUnitsObj::UnitsObj(obj) => {
- Field::UnitsAsStr
- }
- }
- }),
- _ => Err(de::Error::unknown_field(value, FIELDS)),
- }
- }
- }
- deserializer.deserialize_identifier(FieldVisitor)
- }
- }
- struct ParamVisitor;
- impl<'de> Visitor<'de> for ParamVisitor {
- type Value = Param;
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("enum Param")
- }
- fn visit_map<V>(self, mut map: V) -> Result<Param, V::Error>
- where
- V: MapAccess<'de>,
- {
- let mut name = None;
- let mut units_as_string = None;
- let mut units_as_object = None;
- while let Some(key) = map.next_key()? {
- match key {
- Field::Name => {
- if name.is_some() {
- return Err(de::Error::duplicate_field("Name"));
- }
- name = Some(map.next_value()?);
- }
- Field::UnitsAsObj => {
- if units_as_object.is_some() {
- return Err(de::Error::duplicate_field("Units"));
- }
- units_as_object = Some(map.next_value()?);
- }
- Field::UnitsAsStr => {
- if units_as_string.is_some() {
- return Err(de::Error::duplicate_field("Units"));
- }
- units_as_string = Some(map.next_value()?);
- }
- }
- }
- let name = name.ok_or_else(|| de::Error::missing_field("Name"))?;
- if let Some(units_as_object) = units_as_object {
- Ok(Param::Multiple(MultiUnitParam {
- name: name,
- units: units_as_object
- }))
- } else {
- let units_as_string = units_as_string.ok_or_else(|| de::Error::missing_field("Units"))?;
- Ok(Param::Single(SingleUnitParam {
- name: name,
- units: units_as_string
- }))
- }
- }
- }
- const FIELDS: &'static [&'static str] = &["Name", "Units"];
- deserializer.deserialize_struct("Param", FIELDS, ParamVisitor)
- }
- }
- fn main() {
- let json_raw = r#"[
- { "Name": "a single unit param", "Units": "m/s" },
- { "Name": "a multi unit param", "Units": { "Metric": { "Units": "m/s" }, "Imperial": { "Units": "ft/s" } } }
- ]"#;
- let j: Vec<Param> = serde_json::from_str(&json_raw).unwrap();
- match &j[0] {
- Param::Single(p) => {
- assert_eq!(p.name, "a single unit param");
- assert_eq!(p.units, "m/s");
- },
- Param::Multiple(_p) => panic!("Expected SingleUnitParam, actual MultiUnitParam")
- }
- match &j[1] {
- Param::Single(_p) => panic!("Expected MultiUnitParam, actual SingleUnitParam"),
- Param::Multiple(p) => {
- assert_eq!(p.name, "a multi unit param");
- assert_eq!(p.units.metric.units, "m/s");
- assert_eq!(p.units.imperial.units, "ft/s");
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement