Skip to main content

musli_core/
context.rs

1//! Things related to working with contexts.
2
3use core::error::Error;
4use core::fmt;
5use core::str;
6
7use crate::Allocator;
8
9/// Provides ergonomic access to the serialization context.
10///
11/// This is used to among other things report diagnostics.
12///
13/// # Examples
14///
15/// ```
16/// use musli::context;
17/// use musli::json::Encoding;
18/// use musli::{Encode, Decode};
19///
20/// #[derive(Debug, PartialEq, Encode, Decode)]
21/// struct Person {
22///     name: String,
23///     age: u32,
24/// }
25///
26/// let cx = context::new().with_trace();
27/// let encoding = Encoding::new();
28/// let mut data = Vec::new();
29/// encoding.encode_with(&cx, &mut data, &Person {
30///     name: "Aristotle".to_string(),
31///     age: 61,
32/// })?;
33/// let person: Person = encoding.from_slice_with(&cx, &data)?;
34/// assert_eq!(person.name, "Aristotle");
35/// assert_eq!(person.age, 61);
36///
37/// // Now try to decode invalid data and inspect the error report:
38/// let err = encoding.from_str_with::<_, Person>(&cx, "not valid json").unwrap_err();
39/// let report = cx.report();
40/// println!("{report}");
41/// # Ok::<_, musli::context::ErrorMarker>(())
42/// ```
43pub trait Context: Copy {
44    /// Error produced by the context.
45    type Error;
46    /// A mark during processing.
47    type Mark;
48    /// The allocator associated with the context.
49    type Allocator: Allocator;
50
51    /// Clear the state of the context, allowing it to be re-used.
52    fn clear(self);
53
54    /// Advance the context by `n` bytes of input.
55    ///
56    /// This is typically used to move the mark forward as produced by
57    /// [Context::mark].
58    fn advance(self, n: usize);
59
60    /// Return a mark which acts as a checkpoint at the current encoding state.
61    ///
62    /// The context is in a privileged state in that it sees everything, so a
63    /// mark can be quite useful for determining the context of an error.
64    ///
65    /// This typically indicates a byte offset, and is used by
66    /// [`message_at`][Context::message_at] to report a spanned error.
67    fn mark(self) -> Self::Mark;
68
69    /// Restore the state of the context to the specified mark.
70    fn restore(self, mark: &Self::Mark);
71
72    /// Access the underlying allocator.
73    fn alloc(self) -> Self::Allocator;
74
75    /// Generate a map function which maps an error using the `custom` function.
76    #[inline]
77    fn map<E>(self) -> impl FnOnce(E) -> Self::Error
78    where
79        E: 'static + Send + Sync + Error,
80    {
81        move |error| self.custom(error)
82    }
83
84    /// Report a custom error, which is not encapsulated by the error type
85    /// expected by the context. This is essentially a type-erased way of
86    /// reporting error-like things out from the context.
87    fn custom<E>(self, error: E) -> Self::Error
88    where
89        E: 'static + Send + Sync + Error;
90
91    /// Report a message as an error.
92    ///
93    /// This is made available to format custom error messages in `no_std`
94    /// environments. The error message is to be collected by formatting `T`.
95    fn message<M>(self, message: M) -> Self::Error
96    where
97        M: fmt::Display;
98
99    /// Report an error based on a mark.
100    ///
101    /// A mark is generated using [Context::mark] and indicates a prior state.
102    #[inline]
103    fn message_at<M>(self, mark: &Self::Mark, message: M) -> Self::Error
104    where
105        M: fmt::Display,
106    {
107        _ = mark;
108        self.message(message)
109    }
110
111    /// Report an error based on a mark.
112    ///
113    /// A mark is generated using [Context::mark] and indicates a prior state.
114    #[inline]
115    fn custom_at<E>(self, mark: &Self::Mark, message: E) -> Self::Error
116    where
117        E: 'static + Send + Sync + Error,
118    {
119        _ = mark;
120        self.custom(message)
121    }
122
123    /// Enter one level of nesting while decoding.
124    ///
125    /// Decoders call this before decoding the contents of a nested container,
126    /// such as a sequence, a map, a pack, a variant or a present optional
127    /// value, and call [`leave_nesting`] once it has been decoded, whether
128    /// decoding succeeded or not. Since decoding a nested container generally
129    /// recurses, this allows the context to bound recursion by returning an
130    /// error once input is nested too deeply, instead of letting it overflow
131    /// the stack.
132    ///
133    /// The default implementation accepts any depth. [`DefaultContext`]
134    /// limits nesting to 128 levels by default, which can be changed with
135    /// [`with_nesting_limit`] and [`without_nesting_limit`].
136    ///
137    /// An error returned from this method means that the level was not
138    /// entered, so it must not be matched with a call to [`leave_nesting`].
139    ///
140    /// [`leave_nesting`]: Context::leave_nesting
141    /// [`DefaultContext`]: https://docs.rs/musli/latest/musli/context/struct.DefaultContext.html
142    /// [`with_nesting_limit`]: https://docs.rs/musli/latest/musli/context/struct.DefaultContext.html#method.with_nesting_limit
143    /// [`without_nesting_limit`]: https://docs.rs/musli/latest/musli/context/struct.DefaultContext.html#method.without_nesting_limit
144    #[inline]
145    fn enter_nesting(self) -> Result<(), Self::Error> {
146        Ok(())
147    }
148
149    /// Leave a level of nesting previously entered with [`enter_nesting`].
150    ///
151    /// [`enter_nesting`]: Context::enter_nesting
152    #[inline]
153    fn leave_nesting(self) {}
154
155    /// Indicate that we've entered a struct with the given `name`.
156    ///
157    /// The `name` variable corresponds to the identifiers of the struct.
158    ///
159    /// This will be matched with a corresponding call to [`leave_struct`].
160    ///
161    /// [`leave_struct`]: Context::leave_struct
162    #[inline]
163    fn enter_struct(self, type_name: &'static str) {
164        _ = type_name;
165    }
166
167    /// Trace that we've left the last struct that was entered.
168    #[inline]
169    fn leave_struct(self) {}
170
171    /// Indicate that we've entered an enum with the given `name`.
172    ///
173    /// The `name` variable corresponds to the identifiers of the enum.
174    ///
175    /// This will be matched with a corresponding call to [`leave_enum`].
176    ///
177    /// [`leave_enum`]: Context::leave_enum
178    #[inline]
179    fn enter_enum(self, type_name: &'static str) {
180        _ = type_name;
181    }
182
183    /// Trace that we've left the last enum that was entered.
184    #[inline]
185    fn leave_enum(self) {}
186
187    /// Trace that we've entered the given named field.
188    ///
189    /// A named field is part of a regular struct, where the literal field name
190    /// is the `name` argument below, and the musli tag being used for the field
191    /// is the second argument.
192    ///
193    /// This will be matched with a corresponding call to [`leave_field`].
194    ///
195    /// Here `name` is `"field"` and `tag` is `"string"`.
196    ///
197    /// ```
198    /// use musli::{Decode, Encode};
199    ///
200    /// #[derive(Decode, Encode)]
201    /// #[musli(name_all = "name")]
202    /// struct Struct {
203    ///     #[musli(name = "string")]
204    ///     field: String,
205    /// }
206    /// ```
207    ///
208    /// [`leave_field`]: Context::leave_field
209    #[inline]
210    fn enter_named_field<F>(self, type_name: &'static str, field: F)
211    where
212        F: fmt::Display,
213    {
214        _ = type_name;
215        _ = field;
216    }
217
218    /// Trace that we've entered the given unnamed field.
219    ///
220    /// An unnamed field is part of a tuple struct, where the field index is the
221    /// `index` argument below, and the musli tag being used for the field is
222    /// the second argument.
223    ///
224    /// This will be matched with a corresponding call to [`leave_field`].
225    ///
226    /// Here `index` is `0` and `name` is `"string"`.
227    ///
228    /// ```
229    /// use musli::{Decode, Encode};
230    ///
231    /// #[derive(Decode, Encode)]
232    /// #[musli(name_all = "name")]
233    /// struct Struct(#[musli(name = "string")] String);
234    /// ```
235    ///
236    /// [`leave_field`]: Context::leave_field
237    #[inline]
238    fn enter_unnamed_field<F>(self, index: u32, name: F)
239    where
240        F: fmt::Display,
241    {
242        _ = index;
243        _ = name;
244    }
245
246    /// Trace that we've left the last field that was entered.
247    ///
248    /// The `marker` argument will be the same as the one returned from
249    /// [`enter_named_field`] or [`enter_unnamed_field`].
250    ///
251    /// [`enter_named_field`]: Context::enter_named_field
252    /// [`enter_unnamed_field`]: Context::enter_unnamed_field
253    #[inline]
254    fn leave_field(self) {}
255
256    /// Trace that we've entered the given variant in an enum.
257    ///
258    /// A named variant is part of an enum, where the literal variant name is
259    /// the `name` argument below, and the musli tag being used to decode the
260    /// variant is the second argument.
261    ///
262    /// This will be matched with a corresponding call to
263    /// [`leave_variant`] with the same marker provided as an argument as
264    /// the one returned here.
265    ///
266    /// Here `name` is `"field"` and `tag` is `"string"`.
267    ///
268    /// ```
269    /// use musli::{Decode, Encode};
270    ///
271    /// #[derive(Decode, Encode)]
272    /// #[musli(name_all = "name")]
273    /// struct Struct {
274    ///     #[musli(name = "string")]
275    ///     field: String,
276    /// }
277    /// ```
278    ///
279    /// [`leave_variant`]: Context::leave_variant
280    #[inline]
281    fn enter_variant<V>(self, type_name: &'static str, tag: V)
282    where
283        V: fmt::Display,
284    {
285        _ = type_name;
286        _ = tag;
287    }
288
289    /// Trace that we've left the last variant that was entered.
290    ///
291    /// The `marker` argument will be the same as the one returned from
292    /// [`enter_variant`].
293    ///
294    /// [`enter_variant`]: Context::enter_variant
295    #[inline]
296    fn leave_variant(self) {}
297
298    /// Trace a that a map key has been entered.
299    #[inline]
300    fn enter_map_key<K>(self, field: K)
301    where
302        K: fmt::Display,
303    {
304        _ = field;
305    }
306
307    /// Trace that we've left the last map field that was entered.
308    ///
309    /// The `marker` argument will be the same as the one returned from
310    /// [`enter_map_key`].
311    ///
312    /// [`enter_map_key`]: Context::enter_map_key
313    #[inline]
314    fn leave_map_key(self) {}
315
316    /// Trace a sequence field.
317    #[inline]
318    fn enter_sequence_index(self, index: usize) {
319        _ = index;
320    }
321
322    /// Trace that we've left the last sequence index that was entered.
323    ///
324    /// The `marker` argument will be the same as the one returned from
325    /// [`enter_sequence_index`].
326    ///
327    /// [`enter_sequence_index`]: Context::enter_sequence_index
328    #[inline]
329    fn leave_sequence_index(self) {}
330}