deserr 0.6.4

Deserialization library with focus on error handling
Documentation
# Container attributes

### `#[deserr(rename_all = ...)]`

Rename all the fields (if this is a struct) or variants (if this is an enum) according to the given case convention.
The possible values are: `lowercase`, `camelCase`.

If you need more values please open an issue, it's easy to implement and was simply not implemented because it isn't required for Meilisearch at the moment.

<div class="warning">

Unlike `serde`, you don't need to put the double-quotes (`"`) around the name of the case, e.g.: `#[deserr(rename_all = camelCase)]`.

</div>

```rust
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

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

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

### `#[deserr(deny_unknown_fields)]`

Always error during deserialization when encountering unknown fields.
When this attribute is not present, by default unknown fields are silently ignored.

```rust
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug)]
#[deserr(deny_unknown_fields)]
struct Search {
    query: String,
}

let err = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "doggo": "bork" }),
)
.unwrap_err();

assert_eq!(err.to_string(), "Unknown field `doggo`: expected one of `query`");
```


<div class="warning">

Unlike `serde`, with `deserr` you can specify provide a custom function to handle the error.

```rust
use deserr::{Deserr, deserialize, ErrorKind, DeserializeError, ValuePointerRef, take_cf_content, errors::JsonError};
use std::convert::Infallible;
use serde_json::json;

#[derive(Deserr, Debug)]
#[deserr(deny_unknown_fields = unknown_fields_search)]
struct Search {
    query: String,
}

fn unknown_fields_search<E: DeserializeError>(
    field: &str,
    accepted: &[&str],
    location: ValuePointerRef,
) -> E {
    // `E::error` returns a `ControlFlow<E, E>`, which returns the error and indicates
    // whether we should keep accumulating errors or not. However, here we simply
    // want to retrieve the error's value. This is what `take_cf_content` does.
    match field {
        "doggo" => take_cf_content(E::error::<Infallible>(
                None,
                ErrorKind::Unexpected {
                    msg: String::from("can I pet the doggo? uwu")
                },
                location,
            )),
        _ => take_cf_content(E::error::<Infallible>(
            None,
            deserr::ErrorKind::UnknownKey { key: field, accepted },
            location,
        )),
    }
}

let err = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "doggo": "bork" }),
)
.unwrap_err();

assert_eq!(err.to_string(), "Invalid value: can I pet the doggo? uwu");

let err = deserialize::<Search, _, JsonError>(
    json!({ "query": "doggo", "catto": "jorts" }),
)
.unwrap_err();

assert_eq!(err.to_string(), "Unknown field `catto`: expected one of `query`");
```

</div>

### `#[deserr(tag)]`

Externally tag an enum.

<div class="warning">

Deserr does not support internally tagging your enum yet, which means you'll always need to use this attribute if you're deserializing an enum.

</div>

For complete unit enums, deserr can deserialize their value from a string, though.

```rust
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;

#[derive(Deserr, Debug, PartialEq, Eq)]
struct Search {
    query: Query,
}

#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(tag = "type")]
enum Query {
    Single {
        search: String,
    },
    Multi {
        searches: Vec<String>,
    }
}

let data = deserialize::<Search, _, JsonError>(
    json!({ "query": { "type": "Single", "search": "bork" } }),
)
.unwrap();
assert_eq!(data, Search {
    query: Query::Single {
        search: String::from("bork"),
    },
});
```

### `#[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.

- [If your function can fail, consider using `try_from` instead]#deserrtryfrom
- [The field attribute may interests you as well]field.md#deserrfrom

```rust
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)
        }
    }
}

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

let data = deserialize::<Wildcard, _, JsonError>(
    json!("*"),
)
.unwrap();
assert_eq!(data, 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 `AsciiStringError`)

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` directly]#deserrfrom
- [The field attribute may interests you as well]field.md#deserrtryfrom

```rust
use deserr::{Deserr, deserialize, errors::JsonError};
use serde_json::json;
use std::str::FromStr;
use std::fmt;

// Notice how the `try_from` allows us to leverage the deserr limitation on tuple struct.
#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(try_from(&String) = FromStr::from_str -> AsciiStringError)]
struct AsciiString(String);

#[derive(Debug)]
struct AsciiStringError(char);

impl fmt::Display for AsciiStringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Encountered invalid character: `{}`, only ascii characters are accepted",
            self.0
        )
    }
}
impl std::error::Error for AsciiStringError {}

impl FromStr for AsciiString {
    type Err = AsciiStringError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(c) = s.chars().find(|c| !c.is_ascii()) {
            Err(AsciiStringError(c))
        } else {
            Ok(Self(s.to_string()))
        }
    }
}

let data = deserialize::<AsciiString, _, JsonError>(
    json!("doggo"),
)
.unwrap();
assert_eq!(data, AsciiString(String::from("doggo")));

let error = deserialize::<AsciiString, _, JsonError>(
    json!("👉👈"),
)
.unwrap_err();
assert_eq!(error.to_string(), "Invalid value: Encountered invalid character: `👉`, only ascii characters are accepted");
```

### `#[deserr(validate)]`

Validate a structure **after** it has been deserialized.
This is typically useful when your validation logic needs to take multiple fields into account.

```rust
use deserr::{Deserr, DeserializeError, ErrorKind, ValuePointerRef, deserialize, errors::JsonError};
use serde_json::json;
use std::convert::Infallible;

// `__Deserr_E` represents the Error returned by the generated `Deserr` implementation.
#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(validate = validate_range -> __Deserr_E)]
struct Range {
    min: u8,
    max: u8,
}

fn validate_range<E: DeserializeError>(
    range: Range,
    location: ValuePointerRef,
) -> Result<Range, E> {
    if range.min > range.max {
        Err(deserr::take_cf_content(E::error::<Infallible>(
            None,
            ErrorKind::Unexpected {
                msg: format!(
                    "`max` (`{}`) should be greater than `min` (`{}`)",
                    range.max, range.min
                ),
            },
            location,
        )))
    } else {
        Ok(range)
    }
}

let data = deserialize::<Range, _, JsonError>(
    json!({ "min": 2, "max": 4 }),
)
.unwrap();
assert_eq!(data, Range { min: 2, max: 4 });

let error = deserialize::<Range, _, JsonError>(
    json!({ "min": 4, "max": 2 }),
)
.unwrap_err();
assert_eq!(error.to_string(), "Invalid value: `max` (`2`) should be greater than `min` (`4`)");
```

### `#[deserr(error)]`

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

```rust
use deserr::{Deserr, DeserializeError, ValuePointerRef, ErrorKind, deserialize, errors::JsonError};
use serde_json::json;

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

// As we can see, rust is able to infer the error type.
let data = deserialize::<Search, _, _>(
    json!({ "query": "doggo", "limit": 1 }),
)
.unwrap();
assert_eq!(data, Search { query: String::from("doggo"), limit: 1 });
```

### `#[deserr(where_predicate)]`

Let you add `where` clauses to the `Deserr` implementation that deserr will generate.

```rust
use deserr::{Deserr, DeserializeError, MergeWithError, deserialize, errors::JsonError};
use serde_json::json;

// Here we can constraint the generic `__Deserr_E` type used by deserr to implements `MergeWithError`.
// Now instead of constraining the final error type it stays generic if it's able to accumulate with
// with a `JsonError`.
#[derive(Deserr, Debug, PartialEq, Eq)]
#[deserr(where_predicate = __Deserr_E: MergeWithError<JsonError>, where_predicate = A: Deserr<JsonError>)]
struct Search<A> {
    #[deserr(error = JsonError)]
    query: A,
    limit: usize,
}
```

[For simple cases, see also the `needs_predicate` field attribute.](field.md#deserrneedspredicate)