musli_json/
encoding.rs

1//! Module that defines [`Encoding`] whith allows for customization of the
2//! encoding format, and the [`DEFAULT`] encoding configuration.
3
4use core::marker;
5
6#[cfg(feature = "alloc")]
7use alloc::string::String;
8#[cfg(feature = "alloc")]
9use alloc::vec::Vec;
10
11#[cfg(feature = "std")]
12use std::io;
13
14use musli::de::{Decode, Decoder};
15use musli::en::{Encode, Encoder};
16use musli::mode::Text;
17use musli::Context;
18use musli_utils::{FixedBytes, Writer};
19
20use crate::de::JsonDecoder;
21use crate::en::JsonEncoder;
22use crate::error::Error;
23use crate::parser::{Parser, SliceParser};
24
25/// The default configuration.
26pub const DEFAULT: Encoding = Encoding::new();
27
28/// Encode the given value to the given [`Writer`] using the [`DEFAULT`]
29/// configuration.
30#[inline]
31pub fn encode<W, T>(writer: W, value: &T) -> Result<(), Error>
32where
33    W: Writer,
34    T: ?Sized + Encode<Text>,
35{
36    DEFAULT.encode(writer, value)
37}
38
39/// Encode the given value to the given [Write][io::Write] using the [`DEFAULT`]
40/// configuration.
41#[cfg(feature = "std")]
42#[inline]
43pub fn to_writer<W, T>(writer: W, value: &T) -> Result<(), Error>
44where
45    W: io::Write,
46    T: ?Sized + Encode<Text>,
47{
48    DEFAULT.to_writer(writer, value)
49}
50
51/// Encode the given value to a [`Vec`] using the [`DEFAULT`] configuration.
52#[cfg(feature = "alloc")]
53#[inline]
54pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
55where
56    T: ?Sized + Encode<Text>,
57{
58    DEFAULT.to_vec(value)
59}
60
61/// Encode the given value to a [`String`] using the [`DEFAULT`] configuration.
62#[cfg(feature = "alloc")]
63#[inline]
64pub fn to_string<T>(value: &T) -> Result<String, Error>
65where
66    T: ?Sized + Encode<Text>,
67{
68    DEFAULT.to_string(value)
69}
70
71/// Encode the given value to a fixed-size bytes using the [`DEFAULT`]
72/// configuration.
73#[inline]
74pub fn to_fixed_bytes<const N: usize, T>(value: &T) -> Result<FixedBytes<N>, Error>
75where
76    T: ?Sized + Encode<Text>,
77{
78    DEFAULT.to_fixed_bytes::<N, _>(value)
79}
80
81/// Decode the given type `T` from the given [`Parser`] using the [`DEFAULT`]
82/// configuration.
83#[inline]
84pub fn decode<'de, R, T>(reader: R) -> Result<T, Error>
85where
86    R: Parser<'de>,
87    T: Decode<'de, Text>,
88{
89    DEFAULT.decode(reader)
90}
91
92/// Decode the given type `T` from the given string using the [`DEFAULT`]
93/// configuration.
94#[inline]
95pub fn from_str<'de, T>(string: &'de str) -> Result<T, Error>
96where
97    T: Decode<'de, Text>,
98{
99    DEFAULT.from_str(string)
100}
101
102/// Decode the given type `T` from the given slice using the [`DEFAULT`]
103/// configuration.
104#[inline]
105pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result<T, Error>
106where
107    T: Decode<'de, Text>,
108{
109    DEFAULT.from_slice(bytes)
110}
111
112/// Setting up encoding with parameters.
113pub struct Encoding<M = Text> {
114    _marker: marker::PhantomData<M>,
115}
116
117impl Encoding<Text> {
118    /// Construct a new [`Encoding`].
119    ///
120    /// You can modify this using the available factory methods:
121    ///
122    /// ```
123    /// use musli_json::Encoding;
124    /// use musli::{Encode, Decode};
125    ///
126    /// const CONFIG: Encoding<Json> = Encoding::new().with_mode();
127    ///
128    /// // Mode marker indicating that some attributes should
129    /// // only apply when we're decoding in a JSON mode.
130    /// enum Json {}
131    ///
132    /// #[derive(Debug, PartialEq, Encode, Decode)]
133    /// #[musli(mode = Json, name_all = "name")]
134    /// struct Struct<'a> {
135    ///     name: &'a str,
136    ///     age: u32,
137    /// }
138    ///
139    /// let expected = Struct {
140    ///     name: "Aristotle",
141    ///     age: 61,
142    /// };
143    ///
144    /// let out = CONFIG.to_vec(&expected).unwrap();
145    /// println!("{}", core::str::from_utf8(out.as_slice()).unwrap());
146    ///
147    /// let out = musli_json::to_vec(&expected).unwrap();
148    /// println!("{}", core::str::from_utf8(out.as_slice()).unwrap());
149    /// let actual = musli_json::from_slice(out.as_slice()).unwrap();
150    /// assert_eq!(expected, actual);
151    /// ```
152    #[inline]
153    pub const fn new() -> Self {
154        Encoding {
155            _marker: marker::PhantomData,
156        }
157    }
158}
159
160impl<M> Encoding<M> {
161    /// Change the mode of the encoding.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use musli_json::Encoding;
167    ///
168    /// enum Custom {}
169    ///
170    /// const CONFIG: Encoding<Custom> = Encoding::new().with_mode();
171    /// ```
172    pub const fn with_mode<T>(self) -> Encoding<T> {
173        Encoding {
174            _marker: marker::PhantomData,
175        }
176    }
177
178    /// Encode the given value to the given [`Writer`] using the current
179    /// configuration.
180    ///
181    /// This is the same as [`Encoding::encode`] but allows for using a
182    /// configurable [`Context`].
183    #[inline]
184    pub fn encode_with<C, W, T>(self, cx: &C, writer: W, value: &T) -> Result<(), C::Error>
185    where
186        C: ?Sized + Context<Mode = M>,
187        W: Writer,
188        T: ?Sized + Encode<M>,
189    {
190        cx.clear();
191        JsonEncoder::new(cx, writer).encode(value)
192    }
193
194    /// Encode the given value to a [`String`] using the current configuration.
195    #[cfg(feature = "alloc")]
196    #[inline]
197    pub fn to_string<T>(self, value: &T) -> Result<String, Error>
198    where
199        T: ?Sized + Encode<M>,
200    {
201        musli_utils::allocator::with(|alloc| {
202            let cx = musli_utils::context::Same::new(alloc);
203            self.to_string_with(&cx, value)
204        })
205    }
206
207    /// Encode the given value to a [`String`] using the current configuration.
208    ///
209    /// This is the same as [`Encoding::to_string`] but allows for using a
210    /// configurable [`Context`].
211    #[cfg(feature = "alloc")]
212    #[inline]
213    pub fn to_string_with<T, C>(self, cx: &C, value: &T) -> Result<String, C::Error>
214    where
215        C: ?Sized + Context<Mode = M>,
216        T: ?Sized + Encode<M>,
217    {
218        cx.clear();
219        let mut data = Vec::with_capacity(128);
220        JsonEncoder::new(cx, &mut data).encode(value)?;
221        // SAFETY: Encoder is guaranteed to produce valid UTF-8.
222        Ok(unsafe { String::from_utf8_unchecked(data) })
223    }
224
225    /// Decode the given type `T` from the given [`Parser`] using the current
226    /// configuration.
227    #[inline]
228    pub fn decode<'de, P, T>(self, parser: P) -> Result<T, Error>
229    where
230        P: Parser<'de>,
231        T: Decode<'de, M>,
232    {
233        musli_utils::allocator::with(|alloc| {
234            let cx = musli_utils::context::Same::new(alloc);
235            self.decode_with(&cx, parser)
236        })
237    }
238
239    /// Decode the given type `T` from the given [`Parser`] using the current
240    /// configuration.
241    ///
242    /// This is the same as [`Encoding::decode`] but allows for using a
243    /// configurable [`Context`].
244    #[inline]
245    pub fn decode_with<'de, C, P, T>(self, cx: &C, parser: P) -> Result<T, C::Error>
246    where
247        C: ?Sized + Context<Mode = M>,
248        P: Parser<'de>,
249        T: Decode<'de, M>,
250    {
251        cx.clear();
252        JsonDecoder::new(cx, parser).decode()
253    }
254
255    /// Decode the given type `T` from the given string using the current
256    /// configuration.
257    #[inline]
258    pub fn from_str<'de, T>(self, string: &'de str) -> Result<T, Error>
259    where
260        T: Decode<'de, M>,
261    {
262        self.from_slice(string.as_bytes())
263    }
264
265    /// Decode the given type `T` from the given string using the current
266    /// configuration.
267    ///
268    /// This is the same as [`Encoding::from_str`] but allows for using a
269    /// configurable [`Context`].
270    #[inline]
271    pub fn from_str_with<'de, C, T>(self, cx: &C, string: &'de str) -> Result<T, C::Error>
272    where
273        C: ?Sized + Context<Mode = M>,
274        T: Decode<'de, M>,
275    {
276        self.from_slice_with(cx, string.as_bytes())
277    }
278
279    /// Decode the given type `T` from the given slice using the current
280    /// configuration.
281    #[inline]
282    pub fn from_slice<'de, T>(self, bytes: &'de [u8]) -> Result<T, Error>
283    where
284        T: Decode<'de, M>,
285    {
286        musli_utils::allocator::with(|alloc| {
287            let cx = musli_utils::context::Same::<_, M, _>::new(alloc);
288            self.from_slice_with(&cx, bytes)
289        })
290    }
291
292    /// Decode the given type `T` from the given slice using the current
293    /// configuration.
294    ///
295    /// This is the same as [`Encoding::from_slice`] but allows for using a
296    /// configurable [`Context`].
297    #[inline]
298    pub fn from_slice_with<'de, C, T>(self, cx: &C, bytes: &'de [u8]) -> Result<T, C::Error>
299    where
300        C: ?Sized + Context<Mode = M>,
301        T: Decode<'de, M>,
302    {
303        cx.clear();
304        JsonDecoder::new(cx, SliceParser::new(bytes)).decode()
305    }
306
307    musli_utils::encode_with_extensions!(M);
308}
309
310impl<M> Clone for Encoding<M> {
311    #[inline]
312    fn clone(&self) -> Self {
313        *self
314    }
315}
316
317impl<M> Copy for Encoding<M> {}