bytes icon indicating copy to clipboard operation
bytes copied to clipboard

How to handle Vec<Vec<u8>>

Open ghost opened this issue 2 years ago • 1 comments

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.

ghost avatar May 14 '22 16:05 ghost

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())
}

jonasbb avatar May 15 '22 11:05 jonasbb