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//! "InvalidType: 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, Emit};
42//! use deser::State;
43//! use deser::Error;
44//!
45//! struct MyInt(u32);
46//!
47//! impl Serialize for MyInt {
48//! fn serialize<'a>(value: &'a Self, state: &mut State) -> Result<Emit<'a>, 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//! u32::serialize(&value.0, 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: &mut Error, state: &State) {
313 if err.attachment::<Path>().is_some() {
314 return;
315 }
316 if let Some(path) = state.get::<Path>()
317 && !path.segments.is_empty()
318 {
319 err.set_attachment(path.clone());
320 }
321 }
322}