deserr/lib.rs
1#![doc = include_str!("../README.md")]
2#![doc(
3 html_favicon_url = "https://raw.githubusercontent.com/meilisearch/deserr/main/assets/deserr.ico?raw=true"
4)]
5#![doc(
6 html_logo_url = "https://raw.githubusercontent.com/meilisearch/deserr/main/assets/deserr_squared.png?raw=true"
7)]
8
9#[cfg(feature = "actix-web")]
10pub mod actix_web;
11#[cfg(feature = "axum")]
12pub mod axum;
13pub mod errors;
14mod impls;
15#[cfg(feature = "serde-cs")]
16pub mod serde_cs;
17#[cfg(feature = "serde-json")]
18pub mod serde_json;
19mod value;
20
21extern crate self as deserr;
22
23/**
24It is possible to derive the `Deserr` trait for structs and enums with named fields.
25The derive proc macro accept many arguments, explained below:
26
27The basic usage is as follows:
28```
29use deserr::Deserr;
30
31#[derive(Deserr)]
32struct MyStruct {
33 x: bool,
34 y: u8,
35}
36```
37This will implement `impl<E> Deserr<E> MyStruct` for all `E: DeserializeError`.
38
39To use it on enums, the attribute `tag` must be added:
40```
41use deserr::Deserr;
42
43#[derive(Deserr)]
44#[deserr(tag = "my_enum_tag")]
45enum MyEnum {
46 A,
47 B { x: bool, y: u8 }
48}
49```
50This will correctly deserialize the given enum for values of this shape:
51```json
52{
53 "my_enum_tag": "A"
54}
55// or
56{
57 "my_enum_tag": "B",
58 "x": true,
59 "y": 1
60}
61```
62
63It is possible to change the name of the keys corresponding to each field using the `rename` and `rename_all`
64attributes:
65
66```rust
67use deserr::Deserr;
68#[derive(Deserr)]
69#[deserr(rename_all = camelCase)]
70struct MyStruct {
71 my_field: bool,
72 #[deserr(rename = "goodbye_world")]
73 hello_world: u8,
74}
75```
76will parse the following:
77```json
78{
79 "myField": 1,
80 "goodbye_world": 2
81}
82```
83*/
84pub use deserr_internal::Deserr;
85pub use value::{IntoValue, Map, Sequence, Value, ValueKind, ValuePointer, ValuePointerRef};
86
87use std::ops::ControlFlow;
88
89/// A trait for types that can be deserialized from a [`Value`]. The generic type
90/// parameter `E` is the custom error that is returned when deserialization fails.
91pub trait Deserr<E: DeserializeError>: Sized {
92 /// Attempts to deserialize `Self` from the given value. Note that this method is an
93 /// implementation detail. You probably want to use the [`deserialize`] function directly instead.
94 fn deserialize_from_value<V: IntoValue>(
95 value: Value<V>,
96 location: ValuePointerRef,
97 ) -> Result<Self, E>;
98}
99
100/// Deserialize the given value.
101///
102/// This function has three generic arguments, two of which can often be inferred.
103/// 1. `Ret` is the type we want to deserialize to. For example: `MyStruct`
104/// 2. `Val` is the type of the value given as argument. For example: `serde_json::Value`
105/// 3. `E` is the error type we want to get when deserialization fails. For example: `MyError`
106pub fn deserialize<Ret, Val, E>(value: Val) -> Result<Ret, E>
107where
108 Ret: Deserr<E>,
109 Val: IntoValue,
110 E: DeserializeError,
111{
112 Ret::deserialize_from_value(value.into_value(), ValuePointerRef::Origin)
113}
114
115/// A trait which describes how to combine two errors together.
116pub trait MergeWithError<T>: Sized {
117 /// Merge two errors together.
118 ///
119 /// ## Arguments:
120 /// - `self_`: the existing error, if any
121 /// - `other`: the new error
122 /// - `merge_location`: the location where the merging happens.
123 ///
124 /// ## Return value
125 /// It should return the merged error inside a `Result`.
126 ///
127 /// The variant of the returned result should be `Ok(e)` to signal that the deserialization
128 /// should continue (to accumulate more errors), or `Err(e)` to stop the deserialization immediately.
129 ///
130 /// Note that in both cases, the deserialization should eventually fail.
131 ///
132 /// ## Example
133 /// Imagine you have the following json:
134 /// ```json
135 /// {
136 /// "w": true,
137 /// "x" : { "y": 1 }
138 /// }
139 /// ```
140 /// It may be that deserializing the first field, `w`, fails with error `suberror: E`. This is the
141 /// first deserialization error we encounter, so the current error value is `None`. The function `Self::merge`
142 /// is called as follows:
143 /// ```ignore
144 /// // let mut error = None;
145 /// // let mut location : ValuePointerRef::Origin;
146 /// error = Some(Self::merge(error, suberror, location.push_key("w"))?);
147 /// // if the returned value was Err(e), then we returned early from the deserialize method
148 /// // otherwise, `error` is now set
149 /// ```
150 /// Later on, we encounter a new suberror originating from `x.y`. The `merge` function is called again:
151 /// ```ignore
152 /// // let mut error = Some(..);
153 /// // let mut location : ValuePointerRef::Origin;
154 /// error = Some(Self::merge(error, suberror, location.push_key("x"))?);
155 /// // if the returned value was Err(e), then we returned early from the deserialize method
156 /// // otherwise, `error` is now the result of its merging with suberror.
157 /// ```
158 /// Note that even though the suberror originated at `x.y`, the `merge_location` argument was `x`
159 /// because that is where the merge happened.
160 fn merge(
161 self_: Option<Self>,
162 other: T,
163 merge_location: ValuePointerRef,
164 ) -> ControlFlow<Self, Self>;
165}
166
167pub enum ErrorKind<'a, V: IntoValue> {
168 IncorrectValueKind {
169 actual: Value<V>,
170 accepted: &'a [ValueKind],
171 },
172 MissingField {
173 field: &'a str,
174 },
175 UnknownKey {
176 key: &'a str,
177 accepted: &'a [&'a str],
178 },
179 UnknownValue {
180 value: &'a str,
181 accepted: &'a [&'a str],
182 },
183 BadSequenceLen {
184 actual: V::Sequence,
185 expected: usize,
186 },
187 Unexpected {
188 msg: String,
189 },
190}
191
192/// A trait for errors returned by [`deserialize_from_value`](Deserr::deserialize_from_value).
193pub trait DeserializeError: Sized + MergeWithError<Self> {
194 fn error<V: IntoValue>(
195 self_: Option<Self>,
196 error: ErrorKind<V>,
197 location: ValuePointerRef,
198 ) -> ControlFlow<Self, Self>;
199}
200
201/// Used by the derive proc macro. Do not use.
202#[doc(hidden)]
203pub enum FieldState<T> {
204 Missing,
205 Err,
206 Some(T),
207}
208
209impl<T> FieldState<T> {
210 pub fn is_missing(&self) -> bool {
211 matches!(self, FieldState::Missing)
212 }
213
214 #[track_caller]
215 pub fn unwrap(self) -> T {
216 match self {
217 FieldState::Some(x) => x,
218 _ => panic!("Unwrapping an empty field state"),
219 }
220 }
221
222 #[track_caller]
223 pub fn unwrap_or(self, value: T) -> T {
224 match self {
225 FieldState::Some(x) => x,
226 FieldState::Missing => value,
227 FieldState::Err => value,
228 }
229 }
230
231 #[track_caller]
232 pub fn ok_or<E>(self, err: E) -> Result<T, E> {
233 match self {
234 FieldState::Some(x) => Ok(x),
235 FieldState::Missing => Err(err),
236 FieldState::Err => Err(err),
237 }
238 }
239
240 pub fn map<U>(self, f: impl Fn(T) -> U) -> FieldState<U> {
241 match self {
242 FieldState::Some(x) => FieldState::Some(f(x)),
243 FieldState::Missing => FieldState::Missing,
244 FieldState::Err => FieldState::Err,
245 }
246 }
247}
248
249/// Extract the `ControlFlow` result if it's the same type.
250pub fn take_cf_content<T>(r: ControlFlow<T, T>) -> T {
251 match r {
252 ControlFlow::Continue(x) => x,
253 ControlFlow::Break(x) => x,
254 }
255}