bincode-org / bincode

A binary encoder / decoder implementation in Rust.
MIT License
2.63k stars 265 forks source link

Deserialize external tag to separate type variable #680

Closed Phidelux closed 10 months ago

Phidelux commented 10 months ago

I have the following data, which should be (de)serialized using bincode:

#[derive(Deserialize, Serialize)]
#[repr(u8)]
enum DataType {
    Type1,
    Type2,
    Type3,
}

#[serde_as]
#[derive(Deserialize, Serialize)]
#[serde(untagged)]
enum Data {
    Type1(#[serde_as(as = "[_; 64]")] [u8; 64]),
    Type2(#[serde_as(as = "[_; 20]")] [u8; 20]),
    Type3(#[serde_as(as = "[_; 64]")] [u8; 64]),
}

struct Blob {
    …
    payload_type: DataType,
    payload: Data
}

Now I have to ensure that the DataType matches the Data, if possible without doubling the enum discriminant, which should match. How would I implement this?

VictorKoenders commented 10 months ago

Bincode does not support other formats. However you can do something like:

enum Data {
    Type1([u8; 64]),
    Type2([u8; 20]),
    Type3([u8; 64]),
}

impl<'de> serde::de::Deserialize<'de> for Data {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
       where D: Deserializer<'de> {
       let b = u8::deserialize(deserializer)?;
       match b {
           0 => Ok(Data::Type1(<[u8; 64>::deserialize(deserializer)?)),
           1 => Ok(Data::Type2(<[u8; 20>::deserialize(deserializer)?)),
           2 => Ok(Data::Type3(<[u8; 64>::deserialize(deserializer)?)),
           3 => Err(...)
       ]
   }
}

impl Serialize for Data {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
       where S: Serializer {
        match self {
            Self::Type1(b) => {
               0u8.serialize(serializer)?;
               b.serialize(serializer)?;
               Ok(())
            },
            // ...
        }
    }
}