musli/context/
default_context.rs

1use core::error::Error;
2use core::fmt;
3
4#[cfg(feature = "alloc")]
5use crate::alloc::System;
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/// [`System`] 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 [`System`] 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<System, NoTrace, Ignore> {
36    /// Construct the default context which uses the [`System`] allocator for
37    /// memory.
38    #[inline]
39    pub(super) fn new() -> Self {
40        Self::new_in(System::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<System, 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    /// [`report`]: DefaultContext::report
84    /// [`errors`]: DefaultContext::errors
85    /// [`Disabled`]: crate::alloc::Disabled
86    #[inline]
87    pub fn with_trace(self) -> DefaultContext<A, Trace, C> {
88        let trace = Trace::new_in(self.alloc);
89
90        DefaultContext {
91            alloc: self.alloc,
92            trace,
93            capture: self.capture,
94        }
95    }
96
97    /// Capture the specified error type.
98    ///
99    /// This gives access to the last captured error through
100    /// [`DefaultContext::unwrap`] and [`DefaultContext::result`].
101    ///
102    /// Capturing instead of forwarding the error might be beneficial if the
103    /// error type is large.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use musli::{Decode, Encode};
109    /// use musli::alloc::System;
110    /// use musli::context;
111    /// use musli::json::{Encoding, Error};
112    ///
113    /// const ENCODING: Encoding = Encoding::new();
114    ///
115    /// #[derive(Decode, Encode)]
116    /// struct Person {
117    ///     name: String,
118    ///     age: u32,
119    /// }
120    ///
121    /// let cx = context::new().with_capture::<Error>();
122    ///
123    /// let mut data = Vec::new();
124    ///
125    /// ENCODING.encode_with(&cx, &mut data, &Person {
126    ///     name: "Aristotle".to_string(),
127    ///     age: 61,
128    /// })?;
129    ///
130    /// assert!(cx.result().is_ok());
131    ///
132    /// let _: Result<Person, _> = ENCODING.from_slice_with(&cx, &data[..data.len() - 2]);
133    /// assert!(cx.result().is_err());
134    /// Ok::<_, musli::context::ErrorMarker>(())
135    /// ```
136    #[inline]
137    pub fn with_capture<E>(self) -> DefaultContext<A, T, Capture<E>>
138    where
139        E: ContextError<A>,
140    {
141        DefaultContext {
142            alloc: self.alloc,
143            trace: self.trace,
144            capture: Capture::new(),
145        }
146    }
147
148    /// Emit the specified error type `E`.
149    ///
150    /// This causes the method receiving the context to return the specified
151    /// error type directly instead through [`Context::Error`].
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use musli::{Decode, Encode};
157    /// use musli::alloc::System;
158    /// use musli::context;
159    /// use musli::json::{Encoding, Error};
160    ///
161    /// const ENCODING: Encoding = Encoding::new();
162    ///
163    /// #[derive(Decode, Encode)]
164    /// struct Person {
165    ///     name: String,
166    ///     age: u32,
167    /// }
168    ///
169    /// let cx = context::new().with_error();
170    ///
171    /// let mut data = Vec::new();
172    ///
173    /// ENCODING.encode_with(&cx, &mut data, &Person {
174    ///     name: "Aristotle".to_string(),
175    ///     age: 61,
176    /// })?;
177    ///
178    /// let person: Person = ENCODING.from_slice_with(&cx, &data[..])?;
179    /// assert_eq!(person.name, "Aristotle");
180    /// assert_eq!(person.age, 61);
181    /// Ok::<_, Error>(())
182    /// ```
183    #[inline]
184    pub fn with_error<E>(self) -> DefaultContext<A, T, Emit<E>>
185    where
186        E: ContextError<A>,
187    {
188        DefaultContext {
189            alloc: self.alloc,
190            trace: self.trace,
191            capture: Emit::new(),
192        }
193    }
194}
195
196impl<A, C> DefaultContext<A, Trace, C>
197where
198    A: Allocator,
199{
200    /// If tracing is enabled through [`DefaultContext::with_trace`], this
201    /// configured the context to visualize type information, and not just
202    /// variant and fields.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use musli::context;
208    ///
209    /// let cx = context::new().with_trace().with_type();
210    /// ```
211    #[inline]
212    pub fn with_type(mut self) -> Self {
213        self.trace.include_type();
214        self
215    }
216
217    /// Generate a line-separated report of all reported errors.
218    ///
219    /// This can be useful if you want a quick human-readable overview of
220    /// errors. The line separator used will be platform dependent.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use musli::context::{self, ErrorMarker};
226    /// use musli::value::Value;
227    /// use musli::json::Encoding;
228    ///
229    /// const ENCODING: Encoding = Encoding::new();
230    ///
231    /// let cx = context::new().with_trace();
232    ///
233    /// let ErrorMarker = ENCODING.from_str_with::<_, Value<_>>(&cx, "not json").unwrap_err();
234    /// let report = cx.report();
235    /// println!("{report}");
236    /// ```
237    #[inline]
238    pub fn report(&self) -> Report<'_, A> {
239        self.trace.report()
240    }
241
242    /// Iterate over all reported errors.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use musli::context::{self, ErrorMarker};
248    /// use musli::value::Value;
249    /// use musli::json::Encoding;
250    ///
251    /// const ENCODING: Encoding = Encoding::new();
252    ///
253    /// let cx = context::new().with_trace();
254    ///
255    /// let ErrorMarker = ENCODING.from_str_with::<_, Value<_>>(&cx, "not json").unwrap_err();
256    /// assert!(cx.errors().count() > 0);
257    /// ```
258    #[inline]
259    pub fn errors(&self) -> Errors<'_, A> {
260        self.trace.errors()
261    }
262}
263
264impl<A, T, E> DefaultContext<A, T, Capture<E>>
265where
266    A: Allocator,
267    T: TraceMode,
268    E: ContextError<A>,
269{
270    /// Unwrap the error marker or panic if there is no error.
271    #[inline]
272    pub fn unwrap(&self) -> E {
273        self.capture.unwrap()
274    }
275
276    /// Coerce a captured error into a result.
277    #[inline]
278    pub fn result(&self) -> Result<(), E> {
279        self.capture.result()
280    }
281}
282
283impl<A, T, C> Context for &DefaultContext<A, T, C>
284where
285    A: Allocator,
286    T: TraceMode,
287    C: ErrorMode<A>,
288{
289    type Error = C::Error;
290    type Mark = <<T as TraceMode>::Impl<A> as TraceImpl<A>>::Mark;
291    type Allocator = A;
292
293    #[inline]
294    fn clear(self) {
295        self.trace.clear();
296        self.capture.clear();
297    }
298
299    #[inline]
300    fn alloc(self) -> Self::Allocator {
301        self.alloc
302    }
303
304    #[inline]
305    fn custom<E>(self, message: E) -> Self::Error
306    where
307        E: 'static + Send + Sync + Error,
308    {
309        self.trace.custom(self.alloc, &message);
310        self.capture.custom(self.alloc, message)
311    }
312
313    #[inline]
314    fn message<M>(self, message: M) -> Self::Error
315    where
316        M: fmt::Display,
317    {
318        self.trace.message(self.alloc, &message);
319        self.capture.message(self.alloc, message)
320    }
321
322    #[inline]
323    fn message_at<M>(self, mark: &Self::Mark, message: M) -> Self::Error
324    where
325        M: fmt::Display,
326    {
327        self.trace.message_at(self.alloc, mark, &message);
328        self.capture.message(self.alloc, message)
329    }
330
331    #[inline]
332    fn custom_at<E>(self, mark: &Self::Mark, message: E) -> Self::Error
333    where
334        E: 'static + Send + Sync + Error,
335    {
336        self.trace.custom_at(self.alloc, mark, &message);
337        self.capture.custom(self.alloc, message)
338    }
339
340    #[inline]
341    fn mark(self) -> Self::Mark {
342        self.trace.mark()
343    }
344
345    #[inline]
346    fn restore(self, mark: &Self::Mark) {
347        self.trace.restore(mark);
348    }
349
350    #[inline]
351    fn advance(self, n: usize) {
352        self.trace.advance(n);
353    }
354
355    #[inline]
356    fn enter_named_field<F>(self, name: &'static str, field: F)
357    where
358        F: fmt::Display,
359    {
360        self.trace.enter_named_field(name, &field);
361    }
362
363    #[inline]
364    fn enter_unnamed_field<F>(self, index: u32, name: F)
365    where
366        F: fmt::Display,
367    {
368        self.trace.enter_unnamed_field(index, &name);
369    }
370
371    #[inline]
372    fn leave_field(self) {
373        self.trace.leave_field();
374    }
375
376    #[inline]
377    fn enter_struct(self, name: &'static str) {
378        self.trace.enter_struct(name);
379    }
380
381    #[inline]
382    fn leave_struct(self) {
383        self.trace.leave_struct();
384    }
385
386    #[inline]
387    fn enter_enum(self, name: &'static str) {
388        self.trace.enter_enum(name);
389    }
390
391    #[inline]
392    fn leave_enum(self) {
393        self.trace.leave_enum();
394    }
395
396    #[inline]
397    fn enter_variant<V>(self, name: &'static str, tag: V)
398    where
399        V: fmt::Display,
400    {
401        self.trace.enter_variant(name, &tag);
402    }
403
404    #[inline]
405    fn leave_variant(self) {
406        self.trace.leave_variant();
407    }
408
409    #[inline]
410    fn enter_sequence_index(self, index: usize) {
411        self.trace.enter_sequence_index(index);
412    }
413
414    #[inline]
415    fn leave_sequence_index(self) {
416        self.trace.leave_sequence_index();
417    }
418
419    #[inline]
420    fn enter_map_key<F>(self, field: F)
421    where
422        F: fmt::Display,
423    {
424        self.trace.enter_map_key(self.alloc, &field);
425    }
426
427    #[inline]
428    fn leave_map_key(self) {
429        self.trace.leave_map_key();
430    }
431}