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}