1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
//! 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.
//!
//! [`ConstHex`] is an analogue of [`Hex`] that can be used if the serialized buffer has
//! constant length known in compile time.
//!
//! # Crate Features
//!
//! - `alloc` (enabled by default). Enables types that depend on the `alloc` crate:
//! [`Hex`] and [`HexForm`].
//! - `const_len` (disabled by default). Enables types that depend on const generics:
//! [`ConstHex`] and [`ConstHexForm`].
//!
//! [`sodiumoxide`]: https://crates.io/crates/sodiumoxide
//!
//! # 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
//! # unimplemented!()
//! }
//! }
//!
//! 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::{Deserialize, Serialize};
//!
//! # use std::borrow::Cow;
//! struct BufferHex; // a single-purpose type for use in `#[serde(with)]`
//! impl Hex<Buffer> for BufferHex {
//! type Error = &'static str;
//!
//! fn create_bytes(buffer: &Buffer) -> Cow<[u8]> {
//! buffer.as_ref().into()
//! }
//!
//! fn from_bytes(bytes: &[u8]) -> Result<Buffer, Self::Error> {
//! Buffer::from_slice(bytes).ok_or_else(|| "invalid byte length")
//! }
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! pub struct Example {
//! #[serde(with = "BufferHex")]
//! buffer: Buffer,
//! // other fields...
//! }
//!
//! # fn main() {}
//! ```
//!
//! ## 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 serde_derive::*;
//! 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
//! # unimplemented!()
//! }
//! }
//!
//! 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...
//! }
//!
//! # fn main() {}
//! ```
#![no_std]
// Documentation settings.
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(html_root_url = "https://docs.rs/hex-buffer-serde/0.4.0")]
// Linter settings.
#![warn(missing_docs, missing_debug_implementations)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
#[cfg(feature = "const_len")]
mod const_len;
#[cfg(feature = "const_len")]
pub use self::const_len::{ConstHex, ConstHexForm};
#[cfg(feature = "alloc")]
mod var_len;
#[cfg(feature = "alloc")]
pub use self::var_len::{Hex, HexForm};
#[cfg(not(any(feature = "const_len", feature = "alloc")))]
compile_error!(
"At least one of `const_len` and `alloc` features must be enabled; \
the crate is useless otherwise"
);
#[cfg(doctest)]
doc_comment::doctest!("../README.md");