hex_buffer_serde/
lib.rs

1//! Serializing byte buffers as hex strings with `serde`.
2//!
3//! # Problem
4//!
5//! Sometimes, you need to serialize a byte buffer (say, a newtype around `[u8; 32]` or `Vec<u8>`)
6//! as a hex string. The problem is, the newtype in question can be defined in another crate
7//! (for example, cryptographic types from [`sodiumoxide`]), so you can't implement `Serialize` /
8//! `Deserialize` for the type due to Rust orphaning rules. (Or maybe `Serialize` / `Deserialize`
9//! *are* implemented, but not in the desirable way.)
10//!
11//! # Solution
12//!
13//! The core of this crate is the [`Hex`] trait. It provides methods `serialize`
14//! and `deserialize`, which signatures match the ones expected by `serde`. These methods
15//! use the other two required methods of the trait. As all trait methods have no `self` argument,
16//! the trait *can* be implemented for external types; the implementor may be an empty `enum`
17//! designated specifically for this purpose. The implementor can then be used
18//! for (de)serialization with the help of the `#[serde(with)]` attribute.
19//!
20//! [`ConstHex`] is an analogue of [`Hex`] that can be used if the serialized buffer has
21//! constant length known in compile time.
22//!
23//! # Crate Features
24//!
25//! - `alloc` (enabled by default). Enables types that depend on the `alloc` crate:
26//!   [`Hex`] and [`HexForm`].
27//! - `const_len` (disabled by default). Enables types that depend on const generics:
28//!   [`ConstHex`] and [`ConstHexForm`].
29//!
30//! [`sodiumoxide`]: https://crates.io/crates/sodiumoxide
31//!
32//! # Examples
33//!
34//! ```
35//! // Assume this type is defined in an external crate.
36//! pub struct Buffer([u8; 8]);
37//!
38//! impl Buffer {
39//!     pub fn from_slice(slice: &[u8]) -> Option<Self> {
40//!         // snip
41//! #       unimplemented!()
42//!     }
43//! }
44//!
45//! impl AsRef<[u8]> for Buffer {
46//!     fn as_ref(&self) -> &[u8] {
47//!         &self.0
48//!     }
49//! }
50//!
51//! // We define in our crate:
52//! use hex_buffer_serde::Hex;
53//! use serde_derive::{Deserialize, Serialize};
54//!
55//! # use std::borrow::Cow;
56//! struct BufferHex; // a single-purpose type for use in `#[serde(with)]`
57//! impl Hex<Buffer> for BufferHex {
58//!     type Error = &'static str;
59//!
60//!     fn create_bytes(buffer: &Buffer) -> Cow<[u8]> {
61//!         buffer.as_ref().into()
62//!     }
63//!
64//!     fn from_bytes(bytes: &[u8]) -> Result<Buffer, Self::Error> {
65//!         Buffer::from_slice(bytes).ok_or_else(|| "invalid byte length")
66//!     }
67//! }
68//!
69//! #[derive(Serialize, Deserialize)]
70//! pub struct Example {
71//!     #[serde(with = "BufferHex")]
72//!     buffer: Buffer,
73//!     // other fields...
74//! }
75//!
76//! # fn main() {}
77//! ```
78//!
79//! ## Use with internal types
80//!
81//! The crate could still be useful if you have control over the serialized buffer type.
82//! `Hex<T>` has a blanket implementation for types `T` satisfying certain constraints:
83//! `AsRef<[u8]>` and `TryFrom<&[u8]>`. If these constraints are satisfied, you can
84//! use `HexForm::<T>` in `#[serde(with)]`:
85//!
86//! ```
87//! // It is necessary for `Hex` to be in scope in order
88//! // for `serde`-generated code to use its `serialize` / `deserialize` methods.
89//! use hex_buffer_serde::{Hex, HexForm};
90//! # use serde_derive::*;
91//! use core::{array::TryFromSliceError, convert::TryFrom};
92//!
93//! pub struct OurBuffer([u8; 8]);
94//!
95//! impl TryFrom<&[u8]> for OurBuffer {
96//!     type Error = TryFromSliceError;
97//!
98//!     fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
99//!         // snip
100//! #       unimplemented!()
101//!     }
102//! }
103//!
104//! impl AsRef<[u8]> for OurBuffer {
105//!     fn as_ref(&self) -> &[u8] {
106//!         &self.0
107//!     }
108//! }
109//!
110//! #[derive(Serialize, Deserialize)]
111//! pub struct Example {
112//!     #[serde(with = "HexForm::<OurBuffer>")]
113//!     buffer: OurBuffer,
114//!     // other fields...
115//! }
116//!
117//! # fn main() {}
118//! ```
119
120#![no_std]
121// Documentation settings.
122#![cfg_attr(docsrs, feature(doc_cfg))]
123#![doc(html_root_url = "https://docs.rs/hex-buffer-serde/0.4.0")]
124// Linter settings.
125#![warn(missing_docs, missing_debug_implementations)]
126#![warn(clippy::all, clippy::pedantic)]
127#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
128
129#[cfg(any(test, feature = "alloc"))]
130extern crate alloc;
131
132#[cfg(feature = "const_len")]
133mod const_len;
134#[cfg(feature = "const_len")]
135pub use self::const_len::{ConstHex, ConstHexForm};
136#[cfg(feature = "alloc")]
137mod var_len;
138#[cfg(feature = "alloc")]
139pub use self::var_len::{Hex, HexForm};
140
141#[cfg(not(any(feature = "const_len", feature = "alloc")))]
142compile_error!(
143    "At least one of `const_len` and `alloc` features must be enabled; \
144     the crate is useless otherwise"
145);
146
147#[cfg(doctest)]
148doc_comment::doctest!("../README.md");