serde-rs / bytes

Wrapper types to enable optimized handling of &[u8] and Vec<u8>
Apache License 2.0
306 stars 37 forks source link

How to handle Vec<Vec<u8>> #29

Open void-dragon opened 2 years ago

void-dragon commented 2 years ago

Hi, i have a message to send in the form of:

type PubKey = Vec<u8>;

#[derive(Serialize, Deserialize, Debug)]
#[serde(tag="kind")]
pub enum Envelope<T> {
    Greeting {
        #[serde(with="serde_bytes")]
        id: PubKey,
        #[serde(with="serde_bytes")]
        shared: PubKey,
        thin: bool
    },
    AllKnown { 
        all_known: Vec<PubKey>
    },
}

As you can see, PubKey is only a Vec<u8>, what is the most idiomatic way to pack this. I do not want to put the Vec<u8> in a struct to use serde_bytes. Is there something i am missing? It would be really neat to annotated all_known just with #[serde(with="serde_bytes")]. It seems a pretty common problem to be to have an array of byte arrays.

jonasbb commented 2 years ago

serde_bytes only supports a few types and cannot support arbitrary data structures. You can use the serde_with::Bytes type, which is more powerful than what serde_bytes offers and can be used with more collections. The crate documentation shows how you can use it in nested situations.

#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag="kind")]
pub enum Envelope<T> {
    AllKnown { 
        #[serde_as(as = "Vec<serde_with::Bytes>")]
        all_known: Vec<PubKey>
    },
}

If you want to use serde_bytes, you will either need to write the deserialization logic for Vec<PubKey> yourself, for example by deserializing into a Vec<ByteBuf> and turning that into a Vec<PubKey>, or aliasing PubKey to ByteBuf, which already has the serialization behavior you want. Something like this should work:

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<PubKey>, D::Error>
where
    D: Deserializer,
{
    let tmp: Vec<serde_bytes::ByteBuf> = Vec::deserialize(deserializer)?;
    Ok(tmp.into_iter().map(|x| x.into_vec()).collect())
}