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}