Field attributes

#[deserr(rename = "...")]

Deserialize this field with the given name instead of its Rust name.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: String,
    #[deserr(rename = "atr")]
    attributes_to_retrieve: Vec<String>,
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "atr": ["age", "name"] }),
)
.unwrap();
assert_eq!(data, Search {
    query: String::from("doggo"),
    attributes_to_retrieve: vec![String::from("age"), String::from("name")],
});
}

Also available as a variant attribute.

#[deserr(from)]

Deserializing a type from a function instead of a Value. You need to provide the following information;

  1. The input type of the function (here &String)
  2. The path of the function (here, we're simply using the std FromStr implementation)

deserr will first try to deserialize the given type using its Deserr<E> implementation. That means the input type of the from can be complex. Then deserr will call your function.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(from(String) = From::from)]
enum Wildcard {
    Wildcard,
    Value(String),
}

impl From<String> for Wildcard {
    fn from(s: String) -> Self {
        if s == "*" {
            Wildcard::Wildcard
        } else {
            Wildcard::Value(s)
        }
    }
}

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: String,
    #[deserr(from(String) = From::from)]
    field: Wildcard,
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "field": "catto" }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), field: Wildcard::Value(String::from("catto")) });

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "field": "*" }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), field: Wildcard::Wildcard });
}

#[deserr(try_from)]

Try deserializing a type from a function instead of a Value. You need to provide the following information;

  1. The input type of the function (here &String)
  2. The path of the function (here, we're simply using the std FromStr implementation)
  3. The error type that this function can return (here ParseIntError)

deserr will first try to deserialize the given type using its Deserr<E> implementation. That means the input type of the try_from can be complex. Then deserr will call your function and accumulate the specified error against the error type of the caller.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;
use std::convert::Infallible;
use std::str::FromStr;
use std::num::ParseIntError;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: String,
    #[deserr(try_from(&String) = FromStr::from_str -> ParseIntError)]
    limit: usize,

}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "limit": "12" }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), limit: 12 });

let error = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "limit": 12 }),
)
.unwrap_err();
assert_eq!(error.to_string(), "Invalid value type at `.limit`: expected a string, but found a positive integer: `12`");
}

#[deserr(default)]

Allows you to specify a default value for a field.

Note that, unlike serde, by default, Option doesn't automatically use this attribute. Here you need to explicitly define whether your type can get a default value. This makes it less error-prone and easier to make an optional field mandatory.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    #[deserr(default)]
    query: Option<String>,
    #[deserr(default = 20)]
    limit: usize,
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "limit": 4 }),
)
.unwrap();
assert_eq!(data, Search { query: Some(String::from("doggo")), limit: 4 });

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo" }),
)
.unwrap();
assert_eq!(data, Search { query: Some(String::from("doggo")), limit: 20 });
}

#[deserr(skip)]

Allows you to skip the deserialization of a field. It won't show up in the list of fields generated by deny_unknown_fields or in the UnknownKey variant of the ErrorKind type.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: String,
    // A field can be skipped if it implements `Default` or if the `default` attribute is specified.
    #[deserr(skip)]
    hidden: usize,
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo" }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), hidden: 0 });

// if you try to specify the field, it is ignored
let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "hidden": 2 }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), hidden: 0 });

// Here, we're going to see how skip interacts with `deny_unknown_fields`

#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(deny_unknown_fields)]
struct Search2 {
    query: String,
    // A field can be skipped if it implements `Default`.
    #[deserr(skip)]
    hidden: usize,
}

let error = deserialize::<Search2, _, JsonError>(
    json!({ "query": "doggo", "hidden": 1 }),
)
.unwrap_err();
// NOTE: `hidden` isn't in the list of expected fields + `hidden` is effectively considered as a non-existing field.
assert_eq!(error.to_string(), "Unknown field `hidden`: expected one of `query`");
}

#[deserr(map)]

Map a field after it has been deserialized.

#![allow(unused)]
fn main() {
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: String,
    #[deserr(map = add_one)]
    limit: usize,
}

fn add_one(n: usize) -> usize {
    n.saturating_add(1)
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "limit": 0 }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), limit: 1 });

// Let's see how `map` interacts with the `default` attributes.
#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search2 {
    query: String,
    #[deserr(default, map = add_one)]
    limit: usize,
}

let data = deserialize::<Search2, _, JsonError>(
    json!({ "query": "doggo" }),
)
.unwrap();
// As we can see, the `map` attribute is applied AFTER the `default`.
assert_eq!(data, Search2 { query: String::from("doggo"), limit: 1 });
}

#[deserr(missing_field_error)]

Gives you the opportunity to customize the error message if this specific field is missing.

#![allow(unused)]
fn main() {
use deserr::{Deserr, DeserializeError, ValuePointerRef, ErrorKind, deserialize, errors::JsonError};
use serde_json::json;
use std::convert::Infallible;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    #[deserr(missing_field_error = missing_query_field)]
    query: String,
    limit: usize,
}

fn missing_query_field<E: DeserializeError>(_field_name: &str, location: ValuePointerRef) -> E {
    deserr::take_cf_content(E::error::<Infallible>(
        None,
        ErrorKind::Unexpected {
            msg: String::from("I really need the query field, please give it to me uwu"),
        },
        location,
    ))
}

let error = deserialize::<Search, _, JsonError>(
    json!({ "limit": 0 }),
)
.unwrap_err();
assert_eq!(error.to_string(), "Invalid value: I really need the query field, please give it to me uwu");
}

#[deserr(error)]

Customize the error type that can be returned when deserializing this structure instead of keeping it generic.

#![allow(unused)]
fn main() {
use deserr::{Deserr, DeserializeError, ValuePointerRef, ErrorKind, deserialize, errors::JsonError};
use serde_json::json;

// Since the error returned by the `Search` structure needs to implements `MergeWithError<JsonError>`
// we also need to specify the `error` attribute as a `JsonError`. But as you will see later there are
// other solutions.
#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(error = JsonError)]
struct Search<A> {
    #[deserr(error = JsonError)]
    query: A,
    limit: usize,
}
}

#[deserr(needs_predicate)]

Automatically adds where_predicate = FieldType: Deserr<ErrType> for each field with this attribute.

#![allow(unused)]
fn main() {
use deserr::{Deserr, DeserializeError, MergeWithError, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search<A> {
    #[deserr(needs_predicate)]
    query: A,
    limit: usize,
}
}

Is strictly equivalent to the following:

#![allow(unused)]
fn main() {
use deserr::{Deserr, DeserializeError, MergeWithError, deserialize, errors::JsonError};
use serde_json::json;

// `__Deserr_E` represents the Error returned by the generated `Deserr` implementation.
#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(where_predicate = A: Deserr<__Deserr_E>)]
struct Search<A> {
    query: A,
    limit: usize,
}
}