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}