deserr 0.3.0

Deserarialization library with focus on error handling
Documentation

Deserr

Introduction

Deserr is a crate for deserializing data, with the ability to return custom, type-specific errors upon failure.

Unlike serde, Deserr does not parse the data in its serialization format itself, but offload the work to other crates. Instead, it deserializes the already-parsed serialized data into the final type. For example:

// bytes of the serialized value
let s: &str = ".." ;
// parse serialized data using another crate, such as `serde_json`
let json: serde_json::Value = serde_json::from_str(s).unwrap();
// finally deserialize with deserr
let data = T::deserialize_from_value(json.into_value()).unwrap();
// `T` must implements `DeserializeFromValue`.

Thus, Deserr is slower than crates that immediately deserialize a value while parsing at the same time.

The main parts of Deserr are:

  1. [DeserializeFromValue<E>] is the main trait for deserialization
  2. [IntoValue] and [Value] describe the shape that the parsed serialized data must have
  3. [DeserializeError] is the trait that all deserialization errors must conform to
  4. [MergeWithError<E>] describes how to combine multiple errors together. It allows Deserr to return multiple deserialization errors at once.
  5. [ValuePointerRef] and [ValuePointer] point to locations within the value. They are used to locate the origin of an error.
  6. [deserialize] is the main function to use to deserialize a value
  7. The DeserializeFromValue derive proc macro

If the feature serde is activated, then an implementation of [IntoValue] is provided for the type serde_json::Value. This allows using Deserr to deserialize from JSON.

Example

Implementing deserialize for a custom type

use deserr::{DeserializeError, DeserializeFromValue, ErrorKind, DefaultError, Value, ValueKind, IntoValue, MergeWithError, ValuePointerRef, ValuePointer};

enum MyError {
    ForbiddenName,
    Other(DefaultError)
}

impl DeserializeError for MyError {
    /// Create a new error with the custom message.
    ///
    /// Return `Ok` to continue deserializing or `Err` to fail early.
    fn error<V: IntoValue>(_self_: Option<Self>, error: ErrorKind<V>, location: ValuePointerRef) -> Result<Self, Self> {
        Err(Self::Other(DefaultError::error(None, error, location)?))
    }
}

impl From<DefaultError> for MyError {
    fn from(error: DefaultError) -> Self {
        Self::Other(error)
    }
}

impl MergeWithError<MyError> for MyError {
    fn merge(self_: Option<Self>, other: MyError, merge_location: ValuePointerRef) -> Result<Self, Self> {
        Err(other)
    }
}

struct Name(String);

impl DeserializeFromValue<MyError> for Name {
    fn deserialize_from_value<V: IntoValue>(value: Value<V>, location: ValuePointerRef) -> Result<Self, MyError> {
        match value {
            Value::String(s) => {
                if s == "Robert '); DROP TABLE Students; --" {
                    Err(MyError::ForbiddenName)
                } else {
                    Ok(Name(s))
                }
            }
            value => {
                match MyError::error(None, ErrorKind::IncorrectValueKind { actual: value, accepted: &[ValueKind::String] }, location) {
                    Ok(_) => unreachable!(),
                    Err(e) => Err(e),
                }
            }
        }
    }
}

Using macros

#[derive(DeserializeFromValue)]
#[deserr(error = MyError)]
struct User {
	name: Name,
}

License