[][src]Crate hex_buffer_serde

Serializing byte buffers as hex strings with serde.

Problem

Sometimes, you need to serialize a byte buffer (say, a newtype around [u8; 32] or Vec<u8>) as a hex string. The problem is, the newtype in question can be defined in another crate (for example, cryptographic types from sodiumoxide), so you can't implement Serialize / Deserialize for the type due to Rust orphaning rules. (Or maybe Serialize / Deserialize are implemented, but not in the desirable way.)

Solution

The core of this crate is the Hex trait. It provides methods serialize and deserialize, which signatures match the ones expected by serde. These methods use the other two required methods of the trait. As all trait methods have no self argument, the trait can be implemented for external types; the implementor may be an empty enum designated specifically for this purpose. The implementor can then be used for (de)serialization with the help of the #[serde(with)] attribute.

Crate Features

  • std (enabled by default): Enables types from the Rust standard library. Switching this feature off can be used for constrained environments, such as WASM. Note that the crate still requires an allocator (that is, the alloc crate) even if the std feature is disabled.

Examples

// Assume this type is defined in an external crate.
pub struct Buffer([u8; 8]);

impl Buffer {
    pub fn from_slice(slice: &[u8]) -> Option<Self> {
        // snip
    }
}

impl AsRef<[u8]> for Buffer {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

// We define in our crate:
use hex_buffer_serde::Hex;
use serde_derive::*;

enum BufferHex {} // a single-purpose type for use in `#[serde(with)]`
impl Hex<Buffer> for BufferHex {
    fn create_bytes(buffer: &Buffer) -> Cow<[u8]> {
        buffer.as_ref().into()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Buffer, String> {
        Buffer::from_slice(bytes).ok_or_else(|| "invalid byte length".to_owned())
    }
}

#[derive(Serialize, Deserialize)]
pub struct Example {
    #[serde(with = "BufferHex")]
    buffer: Buffer,
    // other fields...
}

Use with internal types

The crate could still be useful if you have control over the serialized buffer type. Hex<T> has a blanket implementation for types T satisfying certain constraints: AsRef<[u8]> and TryFrom<&[u8]>. If these constraints are satisfied, you can use HexForm::<T> in #[serde(with)]:

// It is necessary for `Hex` to be in scope in order
// for `serde`-generated code to use its `serialize` / `deserialize` methods.
use hex_buffer_serde::{Hex, HexForm};
use core::{array::TryFromSliceError, convert::TryFrom};

pub struct OurBuffer([u8; 8]);

impl TryFrom<&[u8]> for OurBuffer {
    type Error = TryFromSliceError;

    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
        // snip
    }
}

impl AsRef<[u8]> for OurBuffer {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

#[derive(Serialize, Deserialize)]
pub struct Example {
    #[serde(with = "HexForm::<OurBuffer>")]
    buffer: OurBuffer,
    // other fields...
}

Structs

HexForm

A dummy container for use inside #[serde(with)] attribute.

Traits

Hex

Provides hex-encoded (de)serialization for serde.