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}