serde_lite/lib.rs
1//! This library provides a bit more lightweight implementation (compared to Serde)
2//! of general-purpose serialization and de-serialization. **The intention here is
3//! not to replace Serde completely** though. Serde does an amazing job and it
4//! wouldn't make much sense to compete with Serde in terms of
5//! serialization/de-serialization speed and the amount of memory used in runtime.
6//!
7//! We focus mainly on the one thing where Serde can be a pain in the... code :) -
8//! and it's the size of the resulting binary. Depending on the complexity of the
9//! types that you try to serialize/de-serialize, Serde can produce quite a lot of
10//! code. Plus there is also some monomorphization which may add even more code to
11//! your binary.
12//!
13//! In order to achieve it's goal, this library does some assumptions about the
14//! underlying format. It uses an intermediate data representation that is similar
15//! to JSON. The intermediate format can be then serialized/deserialized using
16//! Serde. This implies that this library will never be as fast as Serde itself.
17//!
18//! # Usage
19//!
20//! You can use this library as a drop-in replacement for Serde. There are
21//! `Serialize` and `Deserialize` traits that can be automatically derived and
22//! there are also some attributes (compatible with Serde), so all you really have
23//! to do is to put `serde-lite` instead of `serde` into your `Cargo.toml`.
24//!
25//! ## Serialization
26//!
27//! Here is a brief example of serialization into JSON:
28//! ```rust
29//! use serde_lite::Serialize;
30//! use serde_lite_derive::Serialize;
31//!
32//! #[derive(Serialize)]
33//! struct MyStruct {
34//! field1: u32,
35//! field2: String,
36//! }
37//!
38//! let instance = MyStruct {
39//! field1: 10,
40//! field2: String::from("Hello, World!"),
41//! };
42//!
43//! let intermediate = instance.serialize().unwrap();
44//! let json = serde_json::to_string_pretty(&intermediate).unwrap();
45//! ```
46//!
47//! ## De-serialization
48//!
49//! Here is a brief example of de-serialization from JSON:
50//! ```rust
51//! use serde_lite::Deserialize;
52//! use serde_lite_derive::Deserialize;
53//!
54//! #[derive(Deserialize)]
55//! struct MyStruct {
56//! field1: u32,
57//! field2: String,
58//! }
59//!
60//! let input = r#"{
61//! "field1": 10,
62//! "field2": "Hello, World!"
63//! }"#;
64//!
65//! let intermediate = serde_json::from_str(input).unwrap();
66//! let instance = MyStruct::deserialize(&intermediate).unwrap();
67//! ```
68//!
69//! ## Update
70//!
71//! Wait. What? Yes, this library has one more cool feature - partial updates.
72//! Simply derive `Update` the same way you'd derive `Deserialize`. Example:
73//! ```rust
74//! use serde_lite::{Deserialize, Update};
75//! use serde_lite_derive::{Deserialize, Update};
76//!
77//! #[derive(Deserialize, Update)]
78//! struct MyStruct {
79//! field1: u32,
80//! field2: String,
81//! }
82//!
83//! let mut instance = MyStruct {
84//! field1: 10,
85//! field2: String::new(),
86//! };
87//!
88//! let input = r#"{
89//! "field2": "Hello, World!"
90//! }"#;
91//!
92//! let intermediate = serde_json::from_str(input).unwrap();
93//! let instance = instance.update(&intermediate).unwrap();
94//! ```
95//!
96//! This feature can be especially handy if you're constructing a REST API and
97//! you'd like to allow partial updates of your data.
98//!
99//! ## Supported attributes
100//!
101//! The library does not support all Serde attributes at this moment. Patches are
102//! definitely welcome. These attributes are supported:
103//!
104//! * Container attributes:
105//! * `tag`
106//! * `content`
107//! * Field attributes:
108//! * `default`
109//! * `flatten`
110//! * `rename`
111//! * `skip`
112//! * `skip_serializing`
113//! * `skip_serializing_if`
114//! * `skip_deserializing`
115//! * `serialize_with`
116//! * `deserialize_with`
117//! * `update_with`
118//! * Enum variant attributes:
119//! * `rename`
120//!
121//! # When to use this library
122//!
123//! You can use this library whenever you need to serialize/de-serialize some
124//! complex types and the size of the resulting binary matters to you. It is also
125//! very useful in projects where you need to be able to partially update your data
126//! based on the user input (e.g. REST APIs).
127//!
128//! # When to avoid using this library
129//!
130//! If the only thing that matters to you is the runtime performance, you probably
131//! don't want to use this library. It also isn't very useful for
132//! serializing/de-serializing huge amount of data because it needs to be
133//! transformed into the intermediate representation at first. And, finally, this
134//! library can only be used with self-describing formats like JSON.
135
136mod deserialize;
137mod intermediate;
138mod map;
139mod serialize;
140mod update;
141
142use std::{
143 borrow::Cow,
144 collections::LinkedList,
145 fmt::{self, Display, Formatter},
146};
147
148#[cfg(feature = "derive")]
149pub use serde_lite_derive::{Deserialize, Serialize, Update};
150
151pub use crate::{
152 deserialize::Deserialize,
153 intermediate::{Intermediate, Number},
154 map::{Map, MapImpl},
155 serialize::Serialize,
156 update::Update,
157};
158
159/// Error.
160#[derive(Debug, Clone)]
161pub enum Error {
162 OutOfBounds,
163 UnsupportedConversion,
164 MissingField,
165 UnknownEnumVariant,
166 MissingEnumVariantContent,
167 InvalidValue(Cow<'static, str>),
168 NamedFieldErrors(ErrorList<NamedFieldError>),
169 UnnamedFieldErrors(ErrorList<UnnamedFieldError>),
170 Custom(Cow<'static, str>),
171}
172
173impl Error {
174 /// Create an invalid value error with a given expected type name.
175 #[inline]
176 pub fn invalid_value<T>(expected: T) -> Self
177 where
178 T: ToString,
179 {
180 Self::InvalidValue(Cow::Owned(expected.to_string()))
181 }
182
183 /// Create an invalid value error with a given expected type name.
184 #[inline]
185 pub const fn invalid_value_static(expected: &'static str) -> Self {
186 Self::InvalidValue(Cow::Borrowed(expected))
187 }
188
189 /// Create a custom error with a given error message.
190 #[inline]
191 pub fn custom<T>(msg: T) -> Self
192 where
193 T: ToString,
194 {
195 Self::Custom(Cow::Owned(msg.to_string()))
196 }
197
198 /// Create a custom error with a given error message.
199 #[inline]
200 pub const fn custom_static(msg: &'static str) -> Self {
201 Self::Custom(Cow::Borrowed(msg))
202 }
203}
204
205impl Display for Error {
206 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
207 match self {
208 Self::OutOfBounds => f.write_str("value is out of bounds"),
209 Self::UnsupportedConversion => f.write_str("conversion not supported"),
210 Self::MissingField => f.write_str("missing field"),
211 Self::UnknownEnumVariant => f.write_str("unknown enum variant"),
212 Self::MissingEnumVariantContent => f.write_str("missing enum variant content"),
213 Self::InvalidValue(expected) => write!(f, "invalid value ({} expected)", expected),
214 Self::NamedFieldErrors(errors) => {
215 write!(f, "field errors ({})", errors)
216 }
217 Self::UnnamedFieldErrors(errors) => {
218 write!(f, "field errors ({})", errors)
219 }
220 Self::Custom(msg) => f.write_str(msg),
221 }
222 }
223}
224
225impl std::error::Error for Error {}
226
227impl From<ErrorList<NamedFieldError>> for Error {
228 #[inline]
229 fn from(errors: ErrorList<NamedFieldError>) -> Self {
230 Self::NamedFieldErrors(errors)
231 }
232}
233
234impl From<NamedFieldError> for Error {
235 #[inline]
236 fn from(err: NamedFieldError) -> Self {
237 let mut errors = ErrorList::new();
238
239 errors.push(err);
240
241 Self::from(errors)
242 }
243}
244
245impl From<ErrorList<UnnamedFieldError>> for Error {
246 #[inline]
247 fn from(errors: ErrorList<UnnamedFieldError>) -> Self {
248 Self::UnnamedFieldErrors(errors)
249 }
250}
251
252impl From<UnnamedFieldError> for Error {
253 #[inline]
254 fn from(err: UnnamedFieldError) -> Self {
255 let mut errors = ErrorList::new();
256
257 errors.push(err);
258
259 Self::from(errors)
260 }
261}
262
263/// Error associated with a named field.
264#[derive(Debug, Clone)]
265pub struct NamedFieldError {
266 field: Cow<'static, str>,
267 error: Error,
268}
269
270impl NamedFieldError {
271 /// Create a new error for a given named field.
272 #[inline]
273 pub fn new<T>(field: T, error: Error) -> Self
274 where
275 T: ToString,
276 {
277 Self {
278 field: Cow::Owned(field.to_string()),
279 error,
280 }
281 }
282
283 /// Create a new error for a given named field.
284 #[inline]
285 pub const fn new_static(field: &'static str, error: Error) -> Self {
286 Self {
287 field: Cow::Borrowed(field),
288 error,
289 }
290 }
291
292 /// Get the name of the field.
293 #[inline]
294 pub fn field(&self) -> &str {
295 &self.field
296 }
297
298 /// Get the error.
299 #[inline]
300 pub fn error(&self) -> &Error {
301 &self.error
302 }
303
304 /// Take the error.
305 #[inline]
306 pub fn into_error(self) -> Error {
307 self.error
308 }
309}
310
311impl Display for NamedFieldError {
312 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
313 write!(f, "{}: {}", self.field, self.error)
314 }
315}
316
317impl std::error::Error for NamedFieldError {}
318
319/// Error associated with an unnamed field.
320#[derive(Debug, Clone)]
321pub struct UnnamedFieldError {
322 index: usize,
323 error: Error,
324}
325
326impl UnnamedFieldError {
327 /// Create a new error for a given field.
328 #[inline]
329 pub const fn new(field_index: usize, error: Error) -> Self {
330 Self {
331 index: field_index,
332 error,
333 }
334 }
335
336 /// Get index of the field.
337 #[inline]
338 pub fn field_index(&self) -> usize {
339 self.index
340 }
341
342 /// Get the error.
343 #[inline]
344 pub fn error(&self) -> &Error {
345 &self.error
346 }
347
348 /// Take the error.
349 #[inline]
350 pub fn into_error(self) -> Error {
351 self.error
352 }
353}
354
355impl Display for UnnamedFieldError {
356 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
357 write!(f, "{}: {}", self.index, self.error)
358 }
359}
360
361impl std::error::Error for UnnamedFieldError {}
362
363/// List of errors.
364#[derive(Debug, Clone)]
365pub struct ErrorList<T> {
366 inner: LinkedList<T>,
367}
368
369impl<T> ErrorList<T> {
370 /// Create a new error list.
371 #[inline]
372 pub const fn new() -> Self {
373 Self {
374 inner: LinkedList::new(),
375 }
376 }
377
378 /// Check if the list is empty.
379 #[inline]
380 pub fn is_empty(&self) -> bool {
381 self.inner.is_empty()
382 }
383
384 /// Get length of the list.
385 #[inline]
386 pub fn len(&self) -> usize {
387 self.inner.len()
388 }
389
390 /// Add a given error to the list.
391 #[inline]
392 pub fn push(&mut self, err: T) {
393 self.inner.push_back(err)
394 }
395
396 /// Append a given list of errors to the current one.
397 #[inline]
398 pub fn append(&mut self, mut other: Self) {
399 self.inner.append(&mut other.inner)
400 }
401
402 /// Iterate over the errors.
403 #[inline]
404 pub fn iter(&self) -> std::collections::linked_list::Iter<'_, T> {
405 self.inner.iter()
406 }
407}
408
409impl<T> Default for ErrorList<T> {
410 #[inline]
411 fn default() -> Self {
412 Self::new()
413 }
414}
415
416impl<'a, T> IntoIterator for &'a ErrorList<T> {
417 type Item = &'a T;
418 type IntoIter = std::collections::linked_list::Iter<'a, T>;
419
420 #[inline]
421 fn into_iter(self) -> Self::IntoIter {
422 self.inner.iter()
423 }
424}
425
426impl<T> IntoIterator for ErrorList<T> {
427 type Item = T;
428 type IntoIter = std::collections::linked_list::IntoIter<T>;
429
430 #[inline]
431 fn into_iter(self) -> Self::IntoIter {
432 self.inner.into_iter()
433 }
434}
435
436impl<T> Display for ErrorList<T>
437where
438 T: Display,
439{
440 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
441 let mut iter = self.iter();
442
443 if let Some(first) = iter.next() {
444 Display::fmt(first, f)?;
445 }
446
447 for err in iter {
448 write!(f, ", {}", err)?;
449 }
450
451 Ok(())
452 }
453}
454
455impl<T> std::error::Error for ErrorList<T> where T: std::error::Error {}