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    /// Indicate that we've entered a struct with the given `name`.
124    ///
125    /// The `name` variable corresponds to the identifiers of the struct.
126    ///
127    /// This will be matched with a corresponding call to [`leave_struct`].
128    ///
129    /// [`leave_struct`]: Context::leave_struct
130    #[inline]
131    fn enter_struct(self, type_name: &'static str) {
132        _ = type_name;
133    }
134
135    /// Trace that we've left the last struct that was entered.
136    #[inline]
137    fn leave_struct(self) {}
138
139    /// Indicate that we've entered an enum with the given `name`.
140    ///
141    /// The `name` variable corresponds to the identifiers of the enum.
142    ///
143    /// This will be matched with a corresponding call to [`leave_enum`].
144    ///
145    /// [`leave_enum`]: Context::leave_enum
146    #[inline]
147    fn enter_enum(self, type_name: &'static str) {
148        _ = type_name;
149    }
150
151    /// Trace that we've left the last enum that was entered.
152    #[inline]
153    fn leave_enum(self) {}
154
155    /// Trace that we've entered the given named field.
156    ///
157    /// A named field is part of a regular struct, where the literal field name
158    /// is the `name` argument below, and the musli tag being used for the field
159    /// is the second argument.
160    ///
161    /// This will be matched with a corresponding call to [`leave_field`].
162    ///
163    /// Here `name` is `"field"` and `tag` is `"string"`.
164    ///
165    /// ```
166    /// use musli::{Decode, Encode};
167    ///
168    /// #[derive(Decode, Encode)]
169    /// #[musli(name_all = "name")]
170    /// struct Struct {
171    ///     #[musli(name = "string")]
172    ///     field: String,
173    /// }
174    /// ```
175    ///
176    /// [`leave_field`]: Context::leave_field
177    #[inline]
178    fn enter_named_field<F>(self, type_name: &'static str, field: F)
179    where
180        F: fmt::Display,
181    {
182        _ = type_name;
183        _ = field;
184    }
185
186    /// Trace that we've entered the given unnamed field.
187    ///
188    /// An unnamed field is part of a tuple struct, where the field index is the
189    /// `index` argument below, and the musli tag being used for the field is
190    /// the second argument.
191    ///
192    /// This will be matched with a corresponding call to [`leave_field`].
193    ///
194    /// Here `index` is `0` and `name` is `"string"`.
195    ///
196    /// ```
197    /// use musli::{Decode, Encode};
198    ///
199    /// #[derive(Decode, Encode)]
200    /// #[musli(name_all = "name")]
201    /// struct Struct(#[musli(name = "string")] String);
202    /// ```
203    ///
204    /// [`leave_field`]: Context::leave_field
205    #[inline]
206    fn enter_unnamed_field<F>(self, index: u32, name: F)
207    where
208        F: fmt::Display,
209    {
210        _ = index;
211        _ = name;
212    }
213
214    /// Trace that we've left the last field that was entered.
215    ///
216    /// The `marker` argument will be the same as the one returned from
217    /// [`enter_named_field`] or [`enter_unnamed_field`].
218    ///
219    /// [`enter_named_field`]: Context::enter_named_field
220    /// [`enter_unnamed_field`]: Context::enter_unnamed_field
221    #[inline]
222    fn leave_field(self) {}
223
224    /// Trace that we've entered the given variant in an enum.
225    ///
226    /// A named variant is part of an enum, where the literal variant name is
227    /// the `name` argument below, and the musli tag being used to decode the
228    /// variant is the second argument.
229    ///
230    /// This will be matched with a corresponding call to
231    /// [`leave_variant`] with the same marker provided as an argument as
232    /// the one returned here.
233    ///
234    /// Here `name` is `"field"` and `tag` is `"string"`.
235    ///
236    /// ```
237    /// use musli::{Decode, Encode};
238    ///
239    /// #[derive(Decode, Encode)]
240    /// #[musli(name_all = "name")]
241    /// struct Struct {
242    ///     #[musli(name = "string")]
243    ///     field: String,
244    /// }
245    /// ```
246    ///
247    /// [`leave_variant`]: Context::leave_variant
248    #[inline]
249    fn enter_variant<V>(self, type_name: &'static str, tag: V)
250    where
251        V: fmt::Display,
252    {
253        _ = type_name;
254        _ = tag;
255    }
256
257    /// Trace that we've left the last variant that was entered.
258    ///
259    /// The `marker` argument will be the same as the one returned from
260    /// [`enter_variant`].
261    ///
262    /// [`enter_variant`]: Context::enter_variant
263    #[inline]
264    fn leave_variant(self) {}
265
266    /// Trace a that a map key has been entered.
267    #[inline]
268    fn enter_map_key<K>(self, field: K)
269    where
270        K: fmt::Display,
271    {
272        _ = field;
273    }
274
275    /// Trace that we've left the last map field that was entered.
276    ///
277    /// The `marker` argument will be the same as the one returned from
278    /// [`enter_map_key`].
279    ///
280    /// [`enter_map_key`]: Context::enter_map_key
281    #[inline]
282    fn leave_map_key(self) {}
283
284    /// Trace a sequence field.
285    #[inline]
286    fn enter_sequence_index(self, index: usize) {
287        _ = index;
288    }
289
290    /// Trace that we've left the last sequence index that was entered.
291    ///
292    /// The `marker` argument will be the same as the one returned from
293    /// [`enter_sequence_index`].
294    ///
295    /// [`enter_sequence_index`]: Context::enter_sequence_index
296    #[inline]
297    fn leave_sequence_index(self) {}
298}