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");