musli_common/context/
stack_context.rs

1use core::cell::{Cell, UnsafeCell};
2use core::fmt::{self, Write};
3use core::marker::PhantomData;
4use core::ops::Range;
5
6use musli::{Allocator, Context};
7
8use crate::buf::{self, BufString};
9use crate::fixed::FixedVec;
10
11use super::access::{Access, Shared};
12use super::rich_error::{RichError, Step};
13use super::ErrorMarker;
14
15type BufPair<'a, A> = (Range<usize>, BufString<<A as Allocator>::Buf<'a>>);
16
17/// A rich context which uses allocations and tracks the exact location of every
18/// error.
19///
20/// * This only stores the latest error raised.
21/// * The `P` param indicates the maximum number of path steps recorded. If
22///   another step is added it will simply be ignored and an incomplete
23///   indicator is used instead.
24/// * The `S` parameter indicates the maximum size in bytes (UTF-8) of a stored
25///   map key.
26pub struct StackContext<'a, const P: usize, A, M>
27where
28    A: ?Sized + Allocator,
29{
30    access: Access,
31    mark: Cell<usize>,
32    alloc: &'a A,
33    error: UnsafeCell<Option<BufPair<'a, A>>>,
34    path: UnsafeCell<FixedVec<Step<BufString<A::Buf<'a>>>, P>>,
35    path_cap: Cell<usize>,
36    include_type: bool,
37    _marker: PhantomData<M>,
38}
39
40impl<'a, A, M> StackContext<'a, 16, A, M>
41where
42    A: ?Sized + Allocator,
43{
44    /// Construct a new context which uses allocations to a fixed number of
45    /// diagnostics.
46    ///
47    /// This uses the default values of:
48    /// * 16 path elements stored.
49    /// * A maximum map key of 32 bytes (UTF-8).
50    pub fn new(alloc: &'a A) -> Self {
51        Self::new_with(alloc)
52    }
53}
54
55impl<'a, const P: usize, A, M> StackContext<'a, P, A, M>
56where
57    A: ?Sized + Allocator,
58{
59    /// Construct a new context which uses allocations to a fixed but
60    /// configurable number of diagnostics.
61    pub fn new_with(alloc: &'a A) -> Self {
62        Self {
63            access: Access::new(),
64            mark: Cell::new(0),
65            alloc,
66            error: UnsafeCell::new(None),
67            path: UnsafeCell::new(FixedVec::new()),
68            path_cap: Cell::new(0),
69            include_type: false,
70            _marker: PhantomData,
71        }
72    }
73
74    /// Configure the context to visualize type information, and not just
75    /// variant and fields.
76    pub fn include_type(&mut self) -> &mut Self {
77        self.include_type = true;
78        self
79    }
80
81    /// Iterate over all collected errors.
82    pub fn errors(&self) -> Errors<'_, impl fmt::Display + 'a> {
83        let access = self.access.shared();
84
85        Errors {
86            path: unsafe { &*self.path.get() },
87            error: unsafe { (*self.error.get()).as_ref() },
88            path_cap: self.path_cap.get(),
89            _access: access,
90        }
91    }
92
93    /// Push an error into the collection.
94    fn push_error(&self, range: Range<usize>, error: BufString<A::Buf<'a>>) {
95        // SAFETY: We've restricted access to the context, so this is safe.
96        unsafe {
97            self.error.get().replace(Some((range, error)));
98        }
99    }
100
101    /// Push a path.
102    fn push_path(&self, step: Step<BufString<A::Buf<'a>>>) {
103        let _access = self.access.exclusive();
104
105        // SAFETY: We've checked that we have exclusive access just above.
106        let path = unsafe { &mut (*self.path.get()) };
107
108        if path.try_push(step).is_err() {
109            self.path_cap.set(self.path_cap.get() + 1);
110        }
111    }
112
113    /// Pop the last path.
114    fn pop_path(&self) {
115        let cap = self.path_cap.get();
116
117        if cap > 0 {
118            self.path_cap.set(cap - 1);
119            return;
120        }
121
122        let _access = self.access.exclusive();
123
124        // SAFETY: We've checked that we have exclusive access just above.
125        unsafe {
126            (*self.path.get()).pop();
127        }
128    }
129
130    fn format_string<T>(&self, value: T) -> Option<BufString<A::Buf<'a>>>
131    where
132        T: fmt::Display,
133    {
134        let buf = self.alloc.alloc()?;
135        let mut string = BufString::new(buf);
136        write!(string, "{value}").ok()?;
137        Some(string)
138    }
139}
140
141impl<'a, const V: usize, A, M> Context for StackContext<'a, V, A, M>
142where
143    A: ?Sized + Allocator,
144{
145    type Mode = M;
146    type Error = ErrorMarker;
147    type Mark = usize;
148    type Buf<'this> = A::Buf<'this> where Self: 'this;
149    type BufString<'this> = BufString<A::Buf<'this>> where Self: 'this;
150
151    #[inline]
152    fn alloc(&self) -> Option<Self::Buf<'_>> {
153        self.alloc.alloc()
154    }
155
156    #[inline]
157    fn collect_string<T>(&self, value: &T) -> Result<Self::BufString<'_>, Self::Error>
158    where
159        T: ?Sized + fmt::Display,
160    {
161        buf::collect_string(self, value)
162    }
163
164    #[inline]
165    fn custom<T>(&self, message: T) -> Self::Error
166    where
167        T: 'static + Send + Sync + fmt::Display + fmt::Debug,
168    {
169        if let Some(string) = self.format_string(message) {
170            self.push_error(self.mark.get()..self.mark.get(), string);
171        }
172
173        ErrorMarker
174    }
175
176    #[inline]
177    fn message<T>(&self, message: T) -> Self::Error
178    where
179        T: fmt::Display,
180    {
181        if let Some(string) = self.format_string(message) {
182            self.push_error(self.mark.get()..self.mark.get(), string);
183        }
184
185        ErrorMarker
186    }
187
188    #[inline]
189    fn marked_message<T>(&self, mark: Self::Mark, message: T) -> Self::Error
190    where
191        T: fmt::Display,
192    {
193        if let Some(string) = self.format_string(message) {
194            self.push_error(mark..self.mark.get(), string);
195        }
196
197        ErrorMarker
198    }
199
200    #[inline]
201    fn marked_custom<T>(&self, mark: Self::Mark, message: T) -> Self::Error
202    where
203        T: 'static + Send + Sync + fmt::Display + fmt::Debug,
204    {
205        if let Some(string) = self.format_string(message) {
206            self.push_error(mark..self.mark.get(), string);
207        }
208
209        ErrorMarker
210    }
211
212    #[inline]
213    fn mark(&self) -> Self::Mark {
214        self.mark.get()
215    }
216
217    #[inline]
218    fn advance(&self, n: usize) {
219        self.mark.set(self.mark.get().wrapping_add(n));
220    }
221
222    #[inline]
223    fn enter_named_field<T>(&self, name: &'static str, _: &T)
224    where
225        T: ?Sized + fmt::Display,
226    {
227        self.push_path(Step::Named(name));
228    }
229
230    #[inline]
231    fn enter_unnamed_field<T>(&self, index: u32, _: &T)
232    where
233        T: ?Sized + fmt::Display,
234    {
235        self.push_path(Step::Unnamed(index));
236    }
237
238    #[inline]
239    fn leave_field(&self) {
240        self.pop_path();
241    }
242
243    #[inline]
244    fn enter_struct(&self, name: &'static str) {
245        if self.include_type {
246            self.push_path(Step::Struct(name));
247        }
248    }
249
250    #[inline]
251    fn leave_struct(&self) {
252        if self.include_type {
253            self.pop_path();
254        }
255    }
256
257    #[inline]
258    fn enter_enum(&self, name: &'static str) {
259        if self.include_type {
260            self.push_path(Step::Enum(name));
261        }
262    }
263
264    #[inline]
265    fn leave_enum(&self) {
266        if self.include_type {
267            self.pop_path();
268        }
269    }
270
271    #[inline]
272    fn enter_variant<T>(&self, name: &'static str, _: T) {
273        self.push_path(Step::Variant(name));
274    }
275
276    #[inline]
277    fn leave_variant(&self) {
278        self.pop_path();
279    }
280
281    #[inline]
282    fn enter_sequence_index(&self, index: usize) {
283        self.push_path(Step::Index(index));
284    }
285
286    #[inline]
287    fn leave_sequence_index(&self) {
288        self.pop_path();
289    }
290
291    #[inline]
292    fn enter_map_key<T>(&self, field: T)
293    where
294        T: fmt::Display,
295    {
296        if let Some(string) = self.format_string(field) {
297            self.push_path(Step::Key(string));
298        }
299    }
300
301    #[inline]
302    fn leave_map_key(&self) {
303        self.pop_path();
304    }
305}
306
307/// An iterator over available errors.
308pub struct Errors<'a, S> {
309    path: &'a [Step<S>],
310    error: Option<&'a (Range<usize>, S)>,
311    path_cap: usize,
312    _access: Shared<'a>,
313}
314
315impl<'a, S> Iterator for Errors<'a, S> {
316    type Item = RichError<'a, S, S>;
317
318    #[inline]
319    fn next(&mut self) -> Option<Self::Item> {
320        let (range, error) = self.error.take()?;
321
322        Some(RichError::new(
323            self.path,
324            self.path_cap,
325            range.clone(),
326            error,
327        ))
328    }
329}