musli_storage/encoding.rs
1//! Module that defines [`Encoding`] whith allows for customization of the
2//! encoding format, and the [`DEFAULT`] encoding configuration.
3
4#[cfg(feature = "alloc")]
5use alloc::vec::Vec;
6use core::marker;
7#[cfg(feature = "std")]
8use std::io;
9
10use musli::de::Decode;
11use musli::en::Encode;
12use musli::mode::Binary;
13use musli::Context;
14use musli_utils::fixed::FixedBytes;
15use musli_utils::options::{self, Options};
16use musli_utils::{Reader, Writer};
17
18use crate::de::StorageDecoder;
19use crate::en::StorageEncoder;
20use crate::error::Error;
21
22/// Default options to use with [`Encoding`].
23pub const OPTIONS: Options = options::new().build();
24
25/// The default configuration.
26///
27/// Uses variable-encoded numerical fields and variable-encoded prefix lengths.
28///
29/// The variable length encoding uses [`zigzag`] with [`continuation`] encoding
30/// for numbers.
31///
32/// [`zigzag`]: musli_utils::int::zigzag
33/// [`continuation`]: musli_utils::int::continuation
34pub const DEFAULT: Encoding = Encoding::new();
35
36/// Encode the given value to the given [`Writer`] using the [`DEFAULT`]
37/// configuration.
38#[inline]
39pub fn encode<W, T>(writer: W, value: &T) -> Result<(), Error>
40where
41 W: Writer,
42 T: ?Sized + Encode<Binary>,
43{
44 DEFAULT.encode(writer, value)
45}
46
47/// Encode the given value to the given [Write][io::Write] using the [`DEFAULT`]
48/// configuration.
49#[cfg(feature = "std")]
50#[inline]
51pub fn to_writer<W, T>(writer: W, value: &T) -> Result<(), Error>
52where
53 W: io::Write,
54 T: ?Sized + Encode<Binary>,
55{
56 DEFAULT.to_writer(writer, value)
57}
58
59/// Encode the given value to a [`Vec`] using the [`DEFAULT`] configuration.
60#[cfg(feature = "alloc")]
61#[inline]
62pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
63where
64 T: ?Sized + Encode<Binary>,
65{
66 DEFAULT.to_vec(value)
67}
68
69/// Encode the given value to a fixed-size bytes using the [`DEFAULT`]
70/// configuration.
71#[inline]
72pub fn to_fixed_bytes<const N: usize, T>(value: &T) -> Result<FixedBytes<N>, Error>
73where
74 T: ?Sized + Encode<Binary>,
75{
76 DEFAULT.to_fixed_bytes::<N, _>(value)
77}
78
79/// Decode the given type `T` from the given [`Reader`] using the [`DEFAULT`]
80/// configuration.
81#[inline]
82pub fn decode<'de, R, T>(reader: R) -> Result<T, Error>
83where
84 R: Reader<'de>,
85 T: Decode<'de, Binary>,
86{
87 DEFAULT.decode(reader)
88}
89
90/// Decode the given type `T` from the given slice using the [`DEFAULT`]
91/// configuration.
92#[inline]
93pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result<T, Error>
94where
95 T: Decode<'de, Binary>,
96{
97 DEFAULT.from_slice(bytes)
98}
99
100/// Setting up encoding with parameters.
101pub struct Encoding<const OPT: Options = OPTIONS, M = Binary> {
102 _marker: marker::PhantomData<M>,
103}
104
105impl Encoding<OPTIONS, Binary> {
106 /// Construct a new [`Encoding`] instance which uses [`OPTIONS`].
107 ///
108 /// You can modify this behavior by using a custom [`Options`] instance:
109 ///
110 /// ```
111 /// use musli::{Encode, Decode};
112 /// use musli_utils::options::{self, Options, Integer};
113 /// use musli_storage::Encoding;
114 ///
115 /// const OPTIONS: Options = options::new().with_integer(Integer::Fixed).build();
116 /// const CONFIG: Encoding<OPTIONS> = Encoding::new().with_options();
117 ///
118 /// #[derive(Debug, PartialEq, Encode, Decode)]
119 /// struct Struct<'a> {
120 /// name: &'a str,
121 /// age: u32,
122 /// }
123 ///
124 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
125 /// let mut out = Vec::new();
126 ///
127 /// let expected = Struct {
128 /// name: "Aristotle",
129 /// age: 61,
130 /// };
131 ///
132 /// CONFIG.encode(&mut out, &expected)?;
133 /// let actual = CONFIG.decode(&out[..])?;
134 ///
135 /// assert_eq!(expected, actual);
136 /// # Ok(()) }
137 /// ```
138 pub const fn new() -> Self {
139 Encoding {
140 _marker: marker::PhantomData,
141 }
142 }
143}
144
145impl<const OPT: Options, M> Encoding<OPT, M> {
146 /// Change the mode of the encoding.
147 ///
148 /// # Examples
149 ///
150 /// ```rust
151 /// use musli_storage::{OPTIONS, Encoding};
152 ///
153 /// enum Custom {}
154 ///
155 /// const CONFIG: Encoding<OPTIONS, Custom> = Encoding::new().with_mode();
156 /// ```
157 pub const fn with_mode<T>(self) -> Encoding<OPT, T> {
158 Encoding {
159 _marker: marker::PhantomData,
160 }
161 }
162
163 /// Change the options of the encoding.
164 ///
165 /// # Examples
166 ///
167 /// ```
168 /// use musli_utils::options::{self, Options, Integer};
169 /// use musli_storage::Encoding;
170 ///
171 /// const OPTIONS: Options = options::new().with_integer(Integer::Fixed).build();
172 /// const CONFIG: Encoding<OPTIONS> = Encoding::new().with_options();
173 /// ```
174 pub const fn with_options<const U: Options>(self) -> Encoding<U, M> {
175 Encoding {
176 _marker: marker::PhantomData,
177 }
178 }
179
180 musli_utils::encoding_impls!(
181 M,
182 StorageEncoder::<_, OPT, _>::new,
183 StorageDecoder::<_, OPT, _>::new
184 );
185 musli_utils::encoding_from_slice_impls!(M);
186}
187
188impl<const OPT: Options, M> Clone for Encoding<OPT, M> {
189 #[inline]
190 fn clone(&self) -> Self {
191 *self
192 }
193}
194
195impl<const OPT: Options, M> Copy for Encoding<OPT, M> {}