musli_utils/context/
system_context.rs

1use core::cell::{Cell, UnsafeCell};
2use core::fmt;
3use core::marker::PhantomData;
4use core::ops::Range;
5
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9use musli::{Allocator, Context};
10
11use super::access::{self, Access};
12use super::rich_error::{RichError, Step};
13use super::ErrorMarker;
14use crate::buf::{self, BufString};
15
16type BufTriplet<E> = (Vec<Step<String>>, Range<usize>, E);
17
18/// A rich context dynamically allocating space using the system allocator.
19pub struct SystemContext<A, M> {
20    access: Access,
21    mark: Cell<usize>,
22    alloc: A,
23    errors: UnsafeCell<Vec<BufTriplet<String>>>,
24    path: UnsafeCell<Vec<Step<String>>>,
25    include_type: bool,
26    _marker: PhantomData<M>,
27}
28
29impl<A, M> SystemContext<A, M> {
30    /// Construct a new context which uses allocations to store arbitrary
31    /// amounts of diagnostics about decoding.
32    ///
33    /// Or at least until we run out of memory.
34    pub fn new(alloc: A) -> Self {
35        Self {
36            access: Access::new(),
37            mark: Cell::new(0),
38            alloc,
39            errors: UnsafeCell::new(Vec::new()),
40            path: UnsafeCell::new(Vec::new()),
41            include_type: false,
42            _marker: PhantomData,
43        }
44    }
45
46    /// Configure the context to visualize type information, and not just
47    /// variant and fields.
48    pub fn include_type(&mut self) -> &mut Self {
49        self.include_type = true;
50        self
51    }
52
53    /// Generate a line-separated report of all collected errors.
54    pub fn report(&self) -> Report<'_> {
55        Report {
56            errors: self.errors(),
57        }
58    }
59
60    /// Iterate over all collected errors.
61    pub fn errors(&self) -> Errors<'_> {
62        let access = self.access.shared();
63
64        // SAFETY: We've checked above that we have shared access.
65        Errors {
66            errors: unsafe { &*self.errors.get() },
67            index: 0,
68            _access: access,
69        }
70    }
71}
72
73impl<A, M> SystemContext<A, M>
74where
75    A: Allocator,
76{
77    fn push_error(&self, range: Range<usize>, message: String) {
78        let _access = self.access.exclusive();
79
80        // SAFETY: We've restricted access to the context, so this is safe.
81        let path = unsafe { (*self.path.get()).clone() };
82        let errors = unsafe { &mut (*self.errors.get()) };
83
84        errors.push((path, range, message));
85    }
86
87    fn push_path(&self, step: Step<String>) {
88        let _access = self.access.exclusive();
89
90        // SAFETY: We've checked that we have exclusive access just above.
91        let path = unsafe { &mut (*self.path.get()) };
92
93        path.push(step);
94    }
95
96    fn pop_path(&self) {
97        let _access = self.access.exclusive();
98
99        // SAFETY: We've checked that we have exclusive access just above.
100        let path = unsafe { &mut (*self.path.get()) };
101
102        path.pop();
103    }
104}
105
106impl<A, M> Context for SystemContext<A, M>
107where
108    A: Allocator,
109{
110    type Mode = M;
111    type Error = ErrorMarker;
112    type Mark = usize;
113    type Buf<'this> = A::Buf<'this> where Self: 'this;
114    type BufString<'this> = BufString<A::Buf<'this>> where Self: 'this;
115
116    #[inline]
117    fn clear(&self) {
118        self.mark.set(0);
119        let _access = self.access.exclusive();
120
121        // SAFETY: We have acquired exclusive access just above.
122        unsafe {
123            (*self.errors.get()).clear();
124            (*self.path.get()).clear();
125        }
126    }
127
128    #[inline]
129    fn alloc(&self) -> Option<Self::Buf<'_>> {
130        self.alloc.alloc()
131    }
132
133    #[inline]
134    fn collect_string<T>(&self, value: &T) -> Result<Self::BufString<'_>, Self::Error>
135    where
136        T: ?Sized + fmt::Display,
137    {
138        buf::collect_string(self, value)
139    }
140
141    #[inline]
142    fn custom<T>(&self, message: T) -> Self::Error
143    where
144        T: 'static + Send + Sync + fmt::Display + fmt::Debug,
145    {
146        self.push_error(self.mark.get()..self.mark.get(), message.to_string());
147        ErrorMarker
148    }
149
150    #[inline]
151    fn message<T>(&self, message: T) -> Self::Error
152    where
153        T: fmt::Display,
154    {
155        self.push_error(self.mark.get()..self.mark.get(), message.to_string());
156        ErrorMarker
157    }
158
159    #[inline]
160    fn marked_message<T>(&self, mark: Self::Mark, message: T) -> Self::Error
161    where
162        T: fmt::Display,
163    {
164        self.push_error(mark..self.mark.get(), message.to_string());
165        ErrorMarker
166    }
167
168    #[inline]
169    fn marked_custom<T>(&self, mark: Self::Mark, message: T) -> Self::Error
170    where
171        T: 'static + Send + Sync + fmt::Display + fmt::Debug,
172    {
173        self.push_error(mark..self.mark.get(), message.to_string());
174        ErrorMarker
175    }
176
177    #[inline]
178    fn mark(&self) -> Self::Mark {
179        self.mark.get()
180    }
181
182    #[inline]
183    fn advance(&self, n: usize) {
184        self.mark.set(self.mark.get().wrapping_add(n));
185    }
186
187    #[inline]
188    fn enter_named_field<T>(&self, name: &'static str, _: &T)
189    where
190        T: ?Sized + fmt::Display,
191    {
192        self.push_path(Step::Named(name));
193    }
194
195    #[inline]
196    fn enter_unnamed_field<T>(&self, index: u32, _: &T)
197    where
198        T: ?Sized + fmt::Display,
199    {
200        self.push_path(Step::Unnamed(index));
201    }
202
203    #[inline]
204    fn leave_field(&self) {
205        self.pop_path();
206    }
207
208    #[inline]
209    fn enter_struct(&self, name: &'static str) {
210        if self.include_type {
211            self.push_path(Step::Struct(name));
212        }
213    }
214
215    #[inline]
216    fn leave_struct(&self) {
217        if self.include_type {
218            self.pop_path();
219        }
220    }
221
222    #[inline]
223    fn enter_enum(&self, name: &'static str) {
224        if self.include_type {
225            self.push_path(Step::Enum(name));
226        }
227    }
228
229    #[inline]
230    fn leave_enum(&self) {
231        if self.include_type {
232            self.pop_path();
233        }
234    }
235
236    #[inline]
237    fn enter_variant<T>(&self, name: &'static str, _: T) {
238        self.push_path(Step::Variant(name));
239    }
240
241    #[inline]
242    fn leave_variant(&self) {
243        self.pop_path();
244    }
245
246    #[inline]
247    fn enter_sequence_index(&self, index: usize) {
248        self.push_path(Step::Index(index));
249    }
250
251    #[inline]
252    fn leave_sequence_index(&self) {
253        self.pop_path();
254    }
255
256    #[inline]
257    fn enter_map_key<T>(&self, field: T)
258    where
259        T: fmt::Display,
260    {
261        self.push_path(Step::Key(field.to_string()));
262    }
263
264    #[inline]
265    fn leave_map_key(&self) {
266        self.pop_path();
267    }
268}
269
270/// A line-separated report of all errors.
271pub struct Report<'a> {
272    errors: Errors<'a>,
273}
274
275impl fmt::Display for Report<'_> {
276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277        for error in self.errors.clone() {
278            writeln!(f, "{error}")?;
279        }
280
281        Ok(())
282    }
283}
284
285/// An iterator over collected errors.
286#[derive(Clone)]
287pub struct Errors<'a> {
288    errors: &'a [BufTriplet<String>],
289    index: usize,
290    // NB: Drop order is significant, drop the shared access last.
291    _access: access::Shared<'a>,
292}
293
294impl<'a> Iterator for Errors<'a> {
295    type Item = RichError<'a, String, String>;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        let (path, range, error) = self.errors.get(self.index)?;
299        self.index += 1;
300        Some(RichError::new(path, 0, range.clone(), error))
301    }
302}