Skip to content

Serde deserializer for reference content

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

caido/serde-content-ref

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serde Content Ref

github crates

This crate is a public version if the serde private API for content reference deserialization. It is mainly useful to do enum deserialization without cloning the data.

Usage

Since integer tags are not available, you can use that to deserialize versioned structs.

use serde_content_ref::{Content, ContentRefDeserializer};
use serde::Deserialize;

#[derive(Clone, Debug)]
pub struct Edition<const V: u32>;

impl<const V: u32> Edition<V> {
    pub const ERROR: &'static str = "Invalid edition";
}

impl<const V: u32> PartialEq<Edition<V>> for u32 {
    fn eq(&self, _: &Edition<V>) -> bool {
        V == *self
    }
}

impl<'de, const V: u32> Deserialize<'de> for Edition<V> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = u32::deserialize(deserializer)?;
        if value == V {
            Ok(Edition::<V>)
        } else {
            Err(serde::de::Error::custom(Self::ERROR))
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct MyStructV1 {
    pub edition: Edition<1>,
    pub name: String,
    pub value: i32,
}

#[derive(Debug, Deserialize)]
pub struct MyStructV2 {
    pub edition: Edition<2>,
    pub label: String,
    pub amount: f64,
}

#[derive(Debug)]
pub enum ParsableStruct {
    V1(MyStructV1),
    V2(MyStructV2),
}

impl<'de> Deserialize<'de> for ParsableStruct {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let content = Content::deserialize(deserializer)?;

        match MyStructV2::deserialize(ContentRefDeserializer::<D::Error>::new(&content)) {
            Ok(v) => return Ok(ParsableStruct::V2(v)),
            Err(e) if e.to_string() != Edition::<2>::ERROR => return Err(e),
            Err(_) => {},
        }

        match MyStructV1::deserialize(ContentRefDeserializer::<D::Error>::new(&content)) {
            Ok(v) => return Ok(ParsableStruct::V1(v)),
            Err(e) if e.to_string() != Edition::<1>::ERROR => return Err(e),
            Err(_) => {},
        }

        Err(serde::de::Error::custom(
            "data did not match any variant of untagged enum ParsableStruct",
        ))
    }
}

fn main() {
    // V1 JSON contains edition = 1 and v1 fields
    let json_v1 = r#"{"edition": 1, "name": "v1 struct", "value": 42}"#;
    // V2 JSON contains edition = 2 and v2 fields
    let json_v2 = r#"{"edition": 2, "label": "v2 struct", "amount": 101.5}"#;

    let v1: ParsableStruct = serde_json::from_str(json_v1).expect("should parse v1");
    let v2: ParsableStruct = serde_json::from_str(json_v2).expect("should parse v2");

    println!("Parsed v1: {:?}", v1);
    println!("Parsed v2: {:?}", v2);
}

Acknowledgements

This project includes code derived from the Serde project. See NOTICE for more details.

About

Serde deserializer for reference content

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Languages