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;
- The input type of the function (here
&String
) - 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.
- If your function can fail, consider using
try_from
instead - The container attribute may interests you as well
#![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;
- The input type of the function (here
&String
) - The path of the function (here, we're simply using the std
FromStr
implementation) - 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.
- If your function cannot fail, consider using
from
instead - The container attribute may interests you as well
#![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, } }