musli/context/default_context.rs
1use core::error::Error;
2use core::fmt;
3
4#[cfg(feature = "alloc")]
5use crate::alloc::Global;
6use crate::{Allocator, Context};
7
8use super::{
9 Capture, ContextError, Emit, ErrorMode, Errors, Ignore, NoTrace, Report, Trace, TraceImpl,
10 TraceMode,
11};
12
13/// The default context which uses an allocator to track the location of errors.
14///
15/// This is typically constructed using [`new`] and by default uses the
16/// [`Global`] allocator to allocate memory. To customized the allocator to use
17/// [`new_in`] can be used during construction.
18///
19/// The default constructor is only available when the `alloc` feature is
20/// enabled, and will use the [`Global`] allocator.
21///
22/// [`new`]: super::new
23/// [`new_in`]: super::new_in
24pub struct DefaultContext<A, T, C>
25where
26 A: Allocator,
27 T: TraceMode,
28{
29 alloc: A,
30 trace: T::Impl<A>,
31 capture: C,
32}
33
34#[cfg(feature = "alloc")]
35impl DefaultContext<Global, NoTrace, Ignore> {
36 /// Construct the default context which uses the [`Global`] allocator for
37 /// memory.
38 #[inline]
39 pub(super) fn new() -> Self {
40 Self::new_in(Global::new())
41 }
42}
43
44impl<A> DefaultContext<A, NoTrace, Ignore>
45where
46 A: Allocator,
47{
48 /// Construct a new context which uses allocations to a fixed but
49 /// configurable number of diagnostics.
50 #[inline]
51 pub(super) fn new_in(alloc: A) -> Self {
52 let trace = NoTrace::new_in(alloc);
53 Self {
54 alloc,
55 trace,
56 capture: Ignore,
57 }
58 }
59}
60
61#[cfg(feature = "alloc")]
62impl Default for DefaultContext<Global, NoTrace, Ignore> {
63 #[inline]
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<A, T, C> DefaultContext<A, T, C>
70where
71 A: Allocator,
72 T: TraceMode,
73 C: ErrorMode<A>,
74{
75 /// Enable tracing through the current allocator `A`.
76 ///
77 /// Note that this makes diagnostics methods such as [`report`] and
78 /// [`errors`] available on the type.
79 ///
80 /// Tracing requires the configured allocator to work, if for example the
81 /// [`Disabled`] allocator was in use, no diagnostics would be collected.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use musli::context;
87 ///
88 /// let cx = context::new().with_trace();
89 /// // Use cx for encoding/decoding to get detailed error information
90 /// ```
91 ///
92 /// [`report`]: DefaultContext::report
93 /// [`errors`]: DefaultContext::errors
94 /// [`Disabled`]: crate::alloc::Disabled
95 #[inline]
96 pub fn with_trace(self) -> DefaultContext<A, Trace, C> {
97 let trace = Trace::new_in(self.alloc);
98
99 DefaultContext {
100 alloc: self.alloc,
101 trace,
102 capture: self.capture,
103 }
104 }
105
106 /// Capture the specified error type.
107 ///
108 /// This gives access to the last captured error through
109 /// [`DefaultContext::unwrap`] and [`DefaultContext::result`].
110 ///
111 /// Capturing instead of forwarding the error might be beneficial if the
112 /// error type is large.
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use musli::{Decode, Encode};
118 /// use musli::alloc::Global;
119 /// use musli::context;
120 /// use musli::json::{Encoding, Error};
121 ///
122 /// const ENCODING: Encoding = Encoding::new();
123 ///
124 /// #[derive(Decode, Encode)]
125 /// struct Person {
126 /// name: String,
127 /// age: u32,
128 /// }
129 ///
130 /// let cx = context::new().with_capture::<Error>();
131 ///
132 /// let mut data = Vec::new();
133 ///
134 /// ENCODING.encode_with(&cx, &mut data, &Person {
135 /// name: "Aristotle".to_string(),
136 /// age: 61,
137 /// })?;
138 ///
139 /// assert!(cx.result().is_ok());
140 ///
141 /// let _: Result<Person, _> = ENCODING.from_slice_with(&cx, &data[..data.len() - 2]);
142 /// assert!(cx.result().is_err());
143 /// Ok::<_, musli::context::ErrorMarker>(())
144 /// ```
145 #[inline]
146 pub fn with_capture<E>(self) -> DefaultContext<A, T, Capture<E>>
147 where
148 E: ContextError<A>,
149 {
150 DefaultContext {
151 alloc: self.alloc,
152 trace: self.trace,
153 capture: Capture::new(),
154 }
155 }
156
157 /// Emit the specified error type `E`.
158 ///
159 /// This causes the method receiving the context to return the specified
160 /// error type directly instead through [`Context::Error`].
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use musli::{Decode, Encode};
166 /// use musli::alloc::Global;
167 /// use musli::context;
168 /// use musli::json::{Encoding, Error};
169 ///
170 /// const ENCODING: Encoding = Encoding::new();
171 ///
172 /// #[derive(Decode, Encode)]
173 /// struct Person {
174 /// name: String,
175 /// age: u32,
176 /// }
177 ///
178 /// let cx = context::new().with_error();
179 ///
180 /// let mut data = Vec::new();
181 ///
182 /// ENCODING.encode_with(&cx, &mut data, &Person {
183 /// name: "Aristotle".to_string(),
184 /// age: 61,
185 /// })?;
186 ///
187 /// let person: Person = ENCODING.from_slice_with(&cx, &data[..])?;
188 /// assert_eq!(person.name, "Aristotle");
189 /// assert_eq!(person.age, 61);
190 /// Ok::<_, Error>(())
191 /// ```
192 #[inline]
193 pub fn with_error<E>(self) -> DefaultContext<A, T, Emit<E>>
194 where
195 E: ContextError<A>,
196 {
197 DefaultContext {
198 alloc: self.alloc,
199 trace: self.trace,
200 capture: Emit::new(),
201 }
202 }
203}
204
205impl<A, C> DefaultContext<A, Trace, C>
206where
207 A: Allocator,
208{
209 /// If tracing is enabled through [`DefaultContext::with_trace`], this
210 /// configured the context to visualize type information, and not just
211 /// variant and fields.
212 ///
213 /// # Examples
214 ///
215 /// ```
216 /// use musli::context;
217 ///
218 /// let cx = context::new().with_trace().with_type();
219 /// ```
220 #[inline]
221 pub fn with_type(mut self) -> Self {
222 self.trace.include_type();
223 self
224 }
225
226 /// Generate a line-separated report of all reported errors.
227 ///
228 /// This can be useful if you want a quick human-readable overview of
229 /// errors. The line separator used will be platform dependent.
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use musli::context::{self, ErrorMarker};
235 /// use musli::value::Value;
236 /// use musli::json::Encoding;
237 ///
238 /// const ENCODING: Encoding = Encoding::new();
239 ///
240 /// let cx = context::new().with_trace();
241 ///
242 /// let ErrorMarker = ENCODING.from_str_with::<_, Value<_>>(&cx, "not json").unwrap_err();
243 /// let report = cx.report();
244 /// println!("{report}");
245 /// ```
246 #[inline]
247 pub fn report(&self) -> Report<'_, A> {
248 self.trace.report()
249 }
250
251 /// Iterate over all reported errors.
252 ///
253 /// # Examples
254 ///
255 /// ```
256 /// use musli::context::{self, ErrorMarker};
257 /// use musli::value::Value;
258 /// use musli::json::Encoding;
259 ///
260 /// const ENCODING: Encoding = Encoding::new();
261 ///
262 /// let cx = context::new().with_trace();
263 ///
264 /// let ErrorMarker = ENCODING.from_str_with::<_, Value<_>>(&cx, "not json").unwrap_err();
265 /// assert!(cx.errors().count() > 0);
266 /// ```
267 #[inline]
268 pub fn errors(&self) -> Errors<'_, A> {
269 self.trace.errors()
270 }
271}
272
273impl<A, T, E> DefaultContext<A, T, Capture<E>>
274where
275 A: Allocator,
276 T: TraceMode,
277 E: ContextError<A>,
278{
279 /// Unwrap the error marker or panic if there is no error.
280 ///
281 /// # Examples
282 ///
283 /// ```should_panic
284 /// use musli::context;
285 ///
286 /// let cx = context::new().with_capture::<String>();
287 /// // This will panic since no error has been captured
288 /// let error = cx.unwrap();
289 /// ```
290 #[inline]
291 pub fn unwrap(&self) -> E {
292 self.capture.unwrap()
293 }
294
295 /// Coerce a captured error into a result.
296 ///
297 /// # Examples
298 ///
299 /// ```
300 /// use musli::context;
301 ///
302 /// let cx = context::new().with_capture::<String>();
303 /// let result = cx.result();
304 /// assert!(result.is_ok());
305 /// ```
306 #[inline]
307 pub fn result(&self) -> Result<(), E> {
308 self.capture.result()
309 }
310}
311
312impl<A, T, C> Context for &DefaultContext<A, T, C>
313where
314 A: Allocator,
315 T: TraceMode,
316 C: ErrorMode<A>,
317{
318 type Error = C::Error;
319 type Mark = <<T as TraceMode>::Impl<A> as TraceImpl<A>>::Mark;
320 type Allocator = A;
321
322 #[inline]
323 fn clear(self) {
324 self.trace.clear();
325 self.capture.clear();
326 }
327
328 #[inline]
329 fn alloc(self) -> Self::Allocator {
330 self.alloc
331 }
332
333 #[inline]
334 fn custom<E>(self, message: E) -> Self::Error
335 where
336 E: 'static + Send + Sync + Error,
337 {
338 self.trace.custom(self.alloc, &message);
339 self.capture.custom(self.alloc, message)
340 }
341
342 #[inline]
343 fn message<M>(self, message: M) -> Self::Error
344 where
345 M: fmt::Display,
346 {
347 self.trace.message(self.alloc, &message);
348 self.capture.message(self.alloc, message)
349 }
350
351 #[inline]
352 fn message_at<M>(self, mark: &Self::Mark, message: M) -> Self::Error
353 where
354 M: fmt::Display,
355 {
356 self.trace.message_at(self.alloc, mark, &message);
357 self.capture.message(self.alloc, message)
358 }
359
360 #[inline]
361 fn custom_at<E>(self, mark: &Self::Mark, message: E) -> Self::Error
362 where
363 E: 'static + Send + Sync + Error,
364 {
365 self.trace.custom_at(self.alloc, mark, &message);
366 self.capture.custom(self.alloc, message)
367 }
368
369 #[inline]
370 fn mark(self) -> Self::Mark {
371 self.trace.mark()
372 }
373
374 #[inline]
375 fn restore(self, mark: &Self::Mark) {
376 self.trace.restore(mark);
377 }
378
379 #[inline]
380 fn advance(self, n: usize) {
381 self.trace.advance(n);
382 }
383
384 #[inline]
385 fn enter_named_field<F>(self, name: &'static str, field: F)
386 where
387 F: fmt::Display,
388 {
389 self.trace.enter_named_field(name, &field);
390 }
391
392 #[inline]
393 fn enter_unnamed_field<F>(self, index: u32, name: F)
394 where
395 F: fmt::Display,
396 {
397 self.trace.enter_unnamed_field(index, &name);
398 }
399
400 #[inline]
401 fn leave_field(self) {
402 self.trace.leave_field();
403 }
404
405 #[inline]
406 fn enter_struct(self, name: &'static str) {
407 self.trace.enter_struct(name);
408 }
409
410 #[inline]
411 fn leave_struct(self) {
412 self.trace.leave_struct();
413 }
414
415 #[inline]
416 fn enter_enum(self, name: &'static str) {
417 self.trace.enter_enum(name);
418 }
419
420 #[inline]
421 fn leave_enum(self) {
422 self.trace.leave_enum();
423 }
424
425 #[inline]
426 fn enter_variant<V>(self, name: &'static str, tag: V)
427 where
428 V: fmt::Display,
429 {
430 self.trace.enter_variant(name, &tag);
431 }
432
433 #[inline]
434 fn leave_variant(self) {
435 self.trace.leave_variant();
436 }
437
438 #[inline]
439 fn enter_sequence_index(self, index: usize) {
440 self.trace.enter_sequence_index(index);
441 }
442
443 #[inline]
444 fn leave_sequence_index(self) {
445 self.trace.leave_sequence_index();
446 }
447
448 #[inline]
449 fn enter_map_key<F>(self, field: F)
450 where
451 F: fmt::Display,
452 {
453 self.trace.enter_map_key(self.alloc, &field);
454 }
455
456 #[inline]
457 fn leave_map_key(self) {
458 self.trace.leave_map_key();
459 }
460}