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.
12pub trait Context: Copy {
13    /// Error produced by the context.
14    type Error;
15    /// A mark during processing.
16    type Mark;
17    /// The allocator associated with the context.
18    type Allocator: Allocator;
19
20    /// Clear the state of the context, allowing it to be re-used.
21    fn clear(self);
22
23    /// Advance the context by `n` bytes of input.
24    ///
25    /// This is typically used to move the mark forward as produced by
26    /// [Context::mark].
27    fn advance(self, n: usize);
28
29    /// Return a mark which acts as a checkpoint at the current encoding state.
30    ///
31    /// The context is in a privileged state in that it sees everything, so a
32    /// mark can be quite useful for determining the context of an error.
33    ///
34    /// This typically indicates a byte offset, and is used by
35    /// [`message_at`][Context::message_at] to report a spanned error.
36    fn mark(self) -> Self::Mark;
37
38    /// Restore the state of the context to the specified mark.
39    fn restore(self, mark: &Self::Mark);
40
41    /// Access the underlying allocator.
42    fn alloc(self) -> Self::Allocator;
43
44    /// Generate a map function which maps an error using the `custom` function.
45    #[inline]
46    fn map<E>(self) -> impl FnOnce(E) -> Self::Error
47    where
48        E: 'static + Send + Sync + Error,
49    {
50        move |error| self.custom(error)
51    }
52
53    /// Report a custom error, which is not encapsulated by the error type
54    /// expected by the context. This is essentially a type-erased way of
55    /// reporting error-like things out from the context.
56    fn custom<E>(self, error: E) -> Self::Error
57    where
58        E: 'static + Send + Sync + Error;
59
60    /// Report a message as an error.
61    ///
62    /// This is made available to format custom error messages in `no_std`
63    /// environments. The error message is to be collected by formatting `T`.
64    fn message<M>(self, message: M) -> Self::Error
65    where
66        M: fmt::Display;
67
68    /// Report an error based on a mark.
69    ///
70    /// A mark is generated using [Context::mark] and indicates a prior state.
71    #[inline]
72    fn message_at<M>(self, mark: &Self::Mark, message: M) -> Self::Error
73    where
74        M: fmt::Display,
75    {
76        _ = mark;
77        self.message(message)
78    }
79
80    /// Report an error based on a mark.
81    ///
82    /// A mark is generated using [Context::mark] and indicates a prior state.
83    #[inline]
84    fn custom_at<E>(self, mark: &Self::Mark, message: E) -> Self::Error
85    where
86        E: 'static + Send + Sync + Error,
87    {
88        _ = mark;
89        self.custom(message)
90    }
91
92    /// Indicate that we've entered a struct with the given `name`.
93    ///
94    /// The `name` variable corresponds to the identifiers of the struct.
95    ///
96    /// This will be matched with a corresponding call to [`leave_struct`].
97    ///
98    /// [`leave_struct`]: Context::leave_struct
99    #[inline]
100    fn enter_struct(self, type_name: &'static str) {
101        _ = type_name;
102    }
103
104    /// Trace that we've left the last struct that was entered.
105    #[inline]
106    fn leave_struct(self) {}
107
108    /// Indicate that we've entered an enum with the given `name`.
109    ///
110    /// The `name` variable corresponds to the identifiers of the enum.
111    ///
112    /// This will be matched with a corresponding call to [`leave_enum`].
113    ///
114    /// [`leave_enum`]: Context::leave_enum
115    #[inline]
116    fn enter_enum(self, type_name: &'static str) {
117        _ = type_name;
118    }
119
120    /// Trace that we've left the last enum that was entered.
121    #[inline]
122    fn leave_enum(self) {}
123
124    /// Trace that we've entered the given named field.
125    ///
126    /// A named field is part of a regular struct, where the literal field name
127    /// is the `name` argument below, and the musli tag being used for the field
128    /// is the second argument.
129    ///
130    /// This will be matched with a corresponding call to [`leave_field`].
131    ///
132    /// Here `name` is `"field"` and `tag` is `"string"`.
133    ///
134    /// ```
135    /// use musli::{Decode, Encode};
136    ///
137    /// #[derive(Decode, Encode)]
138    /// #[musli(name_all = "name")]
139    /// struct Struct {
140    ///     #[musli(name = "string")]
141    ///     field: String,
142    /// }
143    /// ```
144    ///
145    /// [`leave_field`]: Context::leave_field
146    #[inline]
147    fn enter_named_field<F>(self, type_name: &'static str, field: F)
148    where
149        F: fmt::Display,
150    {
151        _ = type_name;
152        _ = field;
153    }
154
155    /// Trace that we've entered the given unnamed field.
156    ///
157    /// An unnamed field is part of a tuple struct, where the field index is the
158    /// `index` argument below, and the musli tag being used for the field is
159    /// the second argument.
160    ///
161    /// This will be matched with a corresponding call to [`leave_field`].
162    ///
163    /// Here `index` is `0` and `name` is `"string"`.
164    ///
165    /// ```
166    /// use musli::{Decode, Encode};
167    ///
168    /// #[derive(Decode, Encode)]
169    /// #[musli(name_all = "name")]
170    /// struct Struct(#[musli(name = "string")] String);
171    /// ```
172    ///
173    /// [`leave_field`]: Context::leave_field
174    #[inline]
175    fn enter_unnamed_field<F>(self, index: u32, name: F)
176    where
177        F: fmt::Display,
178    {
179        _ = index;
180        _ = name;
181    }
182
183    /// Trace that we've left the last field that was entered.
184    ///
185    /// The `marker` argument will be the same as the one returned from
186    /// [`enter_named_field`] or [`enter_unnamed_field`].
187    ///
188    /// [`enter_named_field`]: Context::enter_named_field
189    /// [`enter_unnamed_field`]: Context::enter_unnamed_field
190    #[inline]
191    fn leave_field(self) {}
192
193    /// Trace that we've entered the given variant in an enum.
194    ///
195    /// A named variant is part of an enum, where the literal variant name is
196    /// the `name` argument below, and the musli tag being used to decode the
197    /// variant is the second argument.
198    ///
199    /// This will be matched with a corresponding call to
200    /// [`leave_variant`] with the same marker provided as an argument as
201    /// the one returned here.
202    ///
203    /// Here `name` is `"field"` and `tag` is `"string"`.
204    ///
205    /// ```
206    /// use musli::{Decode, Encode};
207    ///
208    /// #[derive(Decode, Encode)]
209    /// #[musli(name_all = "name")]
210    /// struct Struct {
211    ///     #[musli(name = "string")]
212    ///     field: String,
213    /// }
214    /// ```
215    ///
216    /// [`leave_variant`]: Context::leave_variant
217    #[inline]
218    fn enter_variant<V>(self, type_name: &'static str, tag: V)
219    where
220        V: fmt::Display,
221    {
222        _ = type_name;
223        _ = tag;
224    }
225
226    /// Trace that we've left the last variant that was entered.
227    ///
228    /// The `marker` argument will be the same as the one returned from
229    /// [`enter_variant`].
230    ///
231    /// [`enter_variant`]: Context::enter_variant
232    #[inline]
233    fn leave_variant(self) {}
234
235    /// Trace a that a map key has been entered.
236    #[inline]
237    fn enter_map_key<K>(self, field: K)
238    where
239        K: fmt::Display,
240    {
241        _ = field;
242    }
243
244    /// Trace that we've left the last map field that was entered.
245    ///
246    /// The `marker` argument will be the same as the one returned from
247    /// [`enter_map_key`].
248    ///
249    /// [`enter_map_key`]: Context::enter_map_key
250    #[inline]
251    fn leave_map_key(self) {}
252
253    /// Trace a sequence field.
254    #[inline]
255    fn enter_sequence_index(self, index: usize) {
256        _ = index;
257    }
258
259    /// Trace that we've left the last sequence index that was entered.
260    ///
261    /// The `marker` argument will be the same as the one returned from
262    /// [`enter_sequence_index`].
263    ///
264    /// [`enter_sequence_index`]: Context::enter_sequence_index
265    #[inline]
266    fn leave_sequence_index(self) {}
267}