serde_path_to_error/
lib.rs

1//! [![github]](https://github.com/dtolnay/path-to-error) [![crates-io]](https://crates.io/crates/serde_path_to_error) [![docs-rs]](https://docs.rs/serde_path_to_error)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! Find out the path at which a deserialization error occurred. This crate
10//! provides a wrapper that works with any existing Serde `Deserializer` and
11//! exposes the chain of field names leading to the error.
12//!
13//! # Example
14//!
15//! ```
16//! # use serde_derive::Deserialize;
17//! #
18//! use serde::Deserialize;
19//! use std::collections::BTreeMap as Map;
20//!
21//! #[derive(Deserialize)]
22//! struct Package {
23//!     name: String,
24//!     dependencies: Map<String, Dependency>,
25//! }
26//!
27//! #[derive(Deserialize)]
28//! struct Dependency {
29//!     version: String,
30//! }
31//!
32//! fn main() {
33//!     let j = r#"{
34//!         "name": "demo",
35//!         "dependencies": {
36//!             "serde": {
37//!                 "version": 1
38//!             }
39//!         }
40//!     }"#;
41//!
42//!     // Some Deserializer.
43//!     let jd = &mut serde_json::Deserializer::from_str(j);
44//!
45//!     let result: Result<Package, _> = serde_path_to_error::deserialize(jd);
46//!     match result {
47//!         Ok(_) => panic!("expected a type error"),
48//!         Err(err) => {
49//!             let path = err.path().to_string();
50//!             assert_eq!(path, "dependencies.serde.version");
51//!         }
52//!     }
53//! }
54//! ```
55
56#![doc(html_root_url = "https://docs.rs/serde_path_to_error/0.1.20")]
57#![no_std]
58#![allow(
59    clippy::doc_link_with_quotes, // https://github.com/rust-lang/rust-clippy/issues/8961
60    clippy::elidable_lifetime_names,
61    clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285
62    clippy::missing_errors_doc,
63    clippy::module_name_repetitions,
64    clippy::must_use_candidate,
65    clippy::needless_lifetimes,
66    clippy::new_without_default,
67    clippy::uninlined_format_args
68)]
69#![allow(unknown_lints, mismatched_lifetime_syntaxes)]
70
71extern crate alloc;
72extern crate serde_core as serde;
73
74mod de;
75mod path;
76mod ser;
77mod wrap;
78
79use alloc::string::String;
80use core::cell::Cell;
81use core::fmt::{self, Display};
82use serde::ser::StdError;
83
84pub use crate::de::{deserialize, Deserializer};
85pub use crate::path::{Path, Segment, Segments};
86pub use crate::ser::{serialize, Serializer};
87
88/// Original deserializer error together with the path at which it occurred.
89#[derive(Clone, Debug)]
90pub struct Error<E> {
91    path: Path,
92    original: E,
93}
94
95impl<E> Error<E> {
96    pub fn new(path: Path, inner: E) -> Self {
97        Error {
98            path,
99            original: inner,
100        }
101    }
102
103    /// Element path at which this deserialization error occurred.
104    pub fn path(&self) -> &Path {
105        &self.path
106    }
107
108    /// The Deserializer's underlying error that occurred.
109    pub fn into_inner(self) -> E {
110        self.original
111    }
112
113    /// Reference to the Deserializer's underlying error that occurred.
114    pub fn inner(&self) -> &E {
115        &self.original
116    }
117}
118
119impl<E: Display> Display for Error<E> {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        if !self.path.is_only_unknown() {
122            write!(f, "{}: ", self.path)?;
123        }
124        write!(f, "{}", self.original)
125    }
126}
127
128impl<E: StdError> StdError for Error<E> {
129    fn source(&self) -> Option<&(dyn StdError + 'static)> {
130        self.original.source()
131    }
132}
133
134/// State for bookkeeping across nested deserializer calls.
135///
136/// You don't need this if you are using `serde_path_to_error::deserializer`. If
137/// you are managing your own `Deserializer`, see the usage example on
138/// [`Deserializer`].
139pub struct Track {
140    path: Cell<Option<Path>>,
141}
142
143impl Track {
144    /// Empty state with no error having happened yet.
145    pub const fn new() -> Self {
146        Track {
147            path: Cell::new(None),
148        }
149    }
150
151    /// Gets path at which the error occurred. Only meaningful after we know
152    /// that an error has occurred. Returns an empty path otherwise.
153    pub fn path(self) -> Path {
154        self.path.into_inner().unwrap_or_else(Path::empty)
155    }
156
157    #[inline]
158    fn trigger<E>(&self, chain: &Chain, err: E) -> E {
159        self.trigger_impl(chain);
160        err
161    }
162
163    fn trigger_impl(&self, chain: &Chain) {
164        self.path.set(Some(match self.path.take() {
165            Some(already_set) => already_set,
166            None => Path::from_chain(chain),
167        }));
168    }
169}
170
171#[derive(Clone)]
172enum Chain<'a> {
173    Root,
174    Seq {
175        parent: &'a Chain<'a>,
176        index: usize,
177    },
178    Map {
179        parent: &'a Chain<'a>,
180        key: String,
181    },
182    Struct {
183        parent: &'a Chain<'a>,
184        key: &'static str,
185    },
186    Enum {
187        parent: &'a Chain<'a>,
188        variant: String,
189    },
190    Some {
191        parent: &'a Chain<'a>,
192    },
193    NewtypeStruct {
194        parent: &'a Chain<'a>,
195    },
196    NewtypeVariant {
197        parent: &'a Chain<'a>,
198    },
199    NonStringKey {
200        parent: &'a Chain<'a>,
201    },
202}