Skip to main content

deser_path/
lib.rs

1//! This crate provides a [`PathLayer`] that tracks the path of the current
2//! value during serialization and deserialization in the
3//! [`State`] (see [`Path`]).
4//!
5//! The layer can be added to both a
6//! [`DeserializeDriver`](deser_core::de::DeserializeDriver) and a
7//! [`SerializeDriver`](deser_core::ser::SerializeDriver).  Types can retrieve
8//! the current [`Path`] from the state and errors get the path of the value
9//! they refer to attached (see [`Error::attachment`]):
10//!
11//! ```rust
12//! use deser_path::{Path, PathLayer, PathSegment};
13//!
14//! #[derive(deser::Deserialize, Debug)]
15//! struct Server {
16//!     host: String,
17//!     port: u16,
18//! }
19//!
20//! let json = r#"[{"host": "a", "port": "80"}]"#;
21//! let err = deser_json::Deserializer::from_str(json)
22//!     .deserialize_with::<Vec<Server>, _>(|driver| {
23//!         driver.push_layer(PathLayer::new())
24//!     })
25//!     .unwrap_err();
26//! let path = err.attachment::<Path>().unwrap();
27//! assert_eq!(path.to_string(), "[0].port");
28//! assert_eq!(path.segments()[0], PathSegment::Index(0));
29//! assert_eq!(
30//!     err.to_string(),
31//!     "Unexpected: unexpected string, expected u16 at line 1 column 24 \
32//!      (path: [0].port)"
33//! );
34//! ```
35//!
36//! During serialization, the path is available to the
37//! [`Serialize`](deser_core::Serialize) implementations:
38//!
39//! ```rust
40//! use deser_path::{Path, PathLayer};
41//! use deser::ser::{Serialize, SerializeDriver, Chunk};
42//! use deser::State;
43//! use deser::Error;
44//!
45//! struct MyInt(u32);
46//!
47//! impl Serialize for MyInt {
48//!     fn serialize(&self, state: &mut State) -> Result<Chunk<'_>, Error> {
49//!         // for as long as the `PathLayer` is added we can at any point
50//!         // request the current path from the state.
51//!         println!("{}", state.get::<Path>().unwrap());
52//!         self.0.serialize(state)
53//!     }
54//! }
55//!
56//! let serializable = vec![MyInt(42), MyInt(23)];
57//! let mut driver = SerializeDriver::new(&serializable);
58//! driver.push_layer(PathLayer::new());
59//! driver.drive(|_event, _state| Ok(())).unwrap();
60//! ```
61#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
62#![no_std]
63
64extern crate alloc;
65
66use alloc::string::String;
67use alloc::vec::Vec;
68use core::fmt;
69
70use deser_core::{Atom, Error, ErrorAttachment, ErrorContext, State};
71
72mod de;
73mod ser;
74
75/// A single segment in the path.
76#[derive(Debug, PartialEq, Eq)]
77pub enum PathSegment {
78    /// An unknown path segment.
79    ///
80    /// This can happen if the key was not a string or unsigned integer.
81    Unknown,
82    /// An unsigned index.
83    Index(usize),
84    /// A string key.
85    Key(String),
86}
87
88impl Clone for PathSegment {
89    fn clone(&self) -> PathSegment {
90        match self {
91            PathSegment::Unknown => PathSegment::Unknown,
92            PathSegment::Index(index) => PathSegment::Index(*index),
93            PathSegment::Key(key) => PathSegment::Key(key.clone()),
94        }
95    }
96
97    fn clone_from(&mut self, source: &PathSegment) {
98        // reuse the buffer of keys
99        match (self, source) {
100            (PathSegment::Key(buf), PathSegment::Key(key)) => buf.clone_from(key),
101            (this, source) => *this = source.clone(),
102        }
103    }
104}
105
106/// The current path of the serialization or deserialization.
107///
108/// This type is stored in the state and can be retrieved at any point.  By
109/// inspecting the [`segments`](Self::segments) a type can figure out where
110/// it's invoked from.  It formats as `servers[1].host`.
111///
112/// The [`PathLayer`] also attaches the path to errors (see
113/// [`Error::attachment`]) where it shows up as `(path: servers[1].host)` in
114/// the error message.
115#[derive(Default)]
116pub struct Path {
117    segments: Vec<PathSegment>,
118    // buffers of popped keys that are reused for new keys
119    spare_keys: Vec<String>,
120}
121
122/// The maximum number of key buffers retained for reuse.
123const MAX_SPARE_KEYS: usize = 32;
124
125impl Path {
126    /// Returns the segments.
127    pub fn segments(&self) -> &[PathSegment] {
128        &self.segments
129    }
130
131    /// Pushes a segment.
132    fn push(&mut self, segment: PathSegment) {
133        self.segments.push(segment);
134    }
135
136    /// Pops a segment and retains the buffer of keys.
137    fn pop(&mut self) {
138        if let Some(PathSegment::Key(buf)) = self.segments.pop() {
139            self.recycle(buf);
140        }
141    }
142
143    /// Sets the last segment.
144    fn set_last(&mut self, segment: PathSegment) {
145        if let Some(last) = self.segments.last_mut() {
146            *last = segment;
147        }
148    }
149
150    /// Sets the last segment to the key in the atom.
151    ///
152    /// This reuses the allocation of the previous key if possible.
153    fn set_last_key(&mut self, atom: &Atom) {
154        if let (Some(PathSegment::Key(buf)), Atom::Str(key) | Atom::Lexical(key)) =
155            (self.segments.last_mut(), atom)
156        {
157            buf.clear();
158            buf.push_str(key);
159            return;
160        }
161        let segment = self.key_segment(atom);
162        if let Some(last) = self.segments.last_mut()
163            && let PathSegment::Key(buf) = core::mem::replace(last, segment)
164        {
165            self.recycle(buf);
166        }
167    }
168
169    /// Returns the segment for a key.
170    fn key_segment(&mut self, atom: &Atom) -> PathSegment {
171        match *atom {
172            Atom::Str(ref key) | Atom::Lexical(ref key) => {
173                let mut buf = self.spare_keys.pop().unwrap_or_default();
174                buf.clear();
175                buf.push_str(key);
176                PathSegment::Key(buf)
177            }
178            Atom::U64(value) => PathSegment::Index(value as usize),
179            Atom::I64(value) if value >= 0 => PathSegment::Index(value as usize),
180            Atom::Ext(ref ext) => {
181                // extension values (like annotated keys) use their fallback
182                match ext.fallback() {
183                    Atom::Ext(_) => PathSegment::Unknown,
184                    fallback => self.key_segment(&fallback),
185                }
186            }
187            // values inferred from text are indexes if they are integers,
188            // otherwise their text is the key
189            Atom::Implicit(ref value) => match value.value().to_atom() {
190                index @ (Atom::U64(_) | Atom::I64(0..)) => self.key_segment(&index),
191                _ => self.key_segment(&Atom::Str(value.text().as_borrowed())),
192            },
193            _ => PathSegment::Unknown,
194        }
195    }
196
197    /// Retains the buffer of a key for reuse.
198    fn recycle(&mut self, buf: String) {
199        if self.spare_keys.len() < MAX_SPARE_KEYS {
200            self.spare_keys.push(buf);
201        }
202    }
203}
204
205impl Clone for Path {
206    fn clone(&self) -> Path {
207        Path {
208            segments: self.segments.clone(),
209            spare_keys: Vec::new(),
210        }
211    }
212
213    fn clone_from(&mut self, source: &Path) {
214        // this is invoked for every replayed event, reuse the memory
215        self.segments.clone_from(&source.segments);
216    }
217}
218
219impl fmt::Debug for Path {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        f.debug_struct("Path")
222            .field("segments", &self.segments)
223            .finish()
224    }
225}
226
227/// Formats the path as `servers[1].host`.
228///
229/// The root path is formatted as an empty string and unknown segments as
230/// `?`.
231impl fmt::Display for Path {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        for (idx, segment) in self.segments.iter().enumerate() {
234            match segment {
235                PathSegment::Key(key) => {
236                    if idx > 0 {
237                        f.write_str(".")?;
238                    }
239                    f.write_str(key)?;
240                }
241                PathSegment::Index(index) => write!(f, "[{}]", index)?,
242                PathSegment::Unknown => {
243                    if idx > 0 {
244                        f.write_str(".")?;
245                    }
246                    f.write_str("?")?;
247                }
248            }
249        }
250        Ok(())
251    }
252}
253
254impl ErrorAttachment for Path {
255    fn fmt_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        write!(f, " (path: {})", self)
257    }
258}
259
260/// A layer that tracks the current [`Path`] in the state.
261///
262/// The layer works for serialization (it implements
263/// [`deser::ser::Layer`](deser_core::ser::Layer)) and deserialization (it implements
264/// [`deser::de::Layer`](deser_core::de::Layer)).  It attaches the path to errors which do not have
265/// one (see [`Error::attachment`]).
266///
267/// During deserialization the path is also correct for values which are
268/// buffered and replayed (for instance by internally tagged enums).
269///
270/// Layers see the path of the current event if they are added after the
271/// path layer.  This also means that the errors of such layers get the path
272/// of the event they reject attached, so the path layer should typically be
273/// added first.
274#[derive(Debug, Default)]
275pub struct PathLayer {
276    frames: Vec<Frame>,
277    registered: bool,
278}
279
280/// A container that is open.
281#[derive(Debug)]
282enum Frame {
283    /// A map, the flag is `true` if a value is expected next (only used
284    /// during serialization).
285    Map(bool),
286    /// A sequence with the index of the next item.
287    Seq(usize),
288}
289
290impl PathLayer {
291    /// Creates a new path layer.
292    pub fn new() -> PathLayer {
293        PathLayer::default()
294    }
295
296    /// Registers the path with the state.
297    #[cold]
298    fn register(&mut self, state: &mut State, replayable: bool) {
299        self.registered = true;
300        state.get_mut::<Path>();
301        if replayable {
302            state.set_replayable::<Path>();
303        }
304        state.add_error_context::<PathContext>();
305    }
306}
307
308/// Attaches the current path to errors.
309struct PathContext;
310
311impl ErrorContext for PathContext {
312    fn add_context(err: Error, state: &State) -> Error {
313        if err.attachment::<Path>().is_some() {
314            return err;
315        }
316        match state.get::<Path>() {
317            Some(path) if !path.segments.is_empty() => err.with_attachment(path.clone()),
318            _ => err,
319        }
320    }
321}