[license]: https://github.com/hashintel/hash/blob/main/libs/deer/LICENSE.md # deer `deer` is an **experimental** backend-agnostic deserialization framework for Rust, featuring meaningful error messages and context (utilizing [`error-stack`](https://crates.io/crates/error-stack)) and a fail-slow behavior by default. ## ⚠️ Disclaimer ⚠️ This crate does not ship with any functionality and is only a name reservation to stop potential name squatting. In the future the actual crate will be published under this name, for the current (incomplete) implementation of the crate please visit the linked repository. ## Fail-Slow Currently available Rust deserializers have mostly been developed with correctness and speed in mind. These are universally beneficial optimizations, but in certain cases (such as when collecting user-facing validation feedback) there are relatively few options available within Rust that allow for extended evaluation beyond a single error. `deer` aims to improve this situation by consciously trading off an acceptable degree of speed to enable the surfacing of multiple errors. ## Example End-user facing APIs are a well-suited example for `deer`. Given the following example: ```rust #[derive(Debug, serde::Deserialize, deer::Deserialize)] struct Body { u8: u8, string: String } fn main() { let payload = json!({ "u8": 256, "string": null, "extra": 1 }); // Note: Syntax is not final! let result = deer::json::from_value::
(payload); let error = result.expect_err("should fail"); println!("{error:?}"); let result = serde_json::from_value::(payload); let error = result.expect_err("should fail"); println!("{error:?}"); } ``` `serde` will fail immediately upon encountering that `256` is larger than what `u8` allows. This leads to frustration for the API consumer, as once they fix that issue the next problem, that `string` cannot be null, will be returned. `serde` also does not include path information about where the issue is located, `deer` does! `deer` solves this problem, by returning every issue present. This means that a single API call with the payload given will result in the errors: `256 larger than u8::MAX`, `null is not String`, and `extra key "extra" provided`. This in turn also means that `deer` can be used to implement custom validation while deserializing, while still being able to return all validation issues. `deer` might provide a way in the future to describe these constraints. ## Limitations `deer` currently does **not** parse values itself, but relies on external parsers like `serde_json`, this means that parsing will be fail-fast, and `deer` only touches syntactically correct values. ## Future Plans The first release of `deer` is intentionally minimal and tries to lay a good foundation to extend functionality in the future. There are many future possible directions and ideas we're trying to see if they can benefit the different use cases. ### Introspection Support Currently, popular crates like `serde` do not provide a way to introspect what the output will be. Other tools try to fill the gap by manually interpreting the instructions given to these crates. This often leads to edge cases, resulting in the dissonance between the expected value and reality. The goal with the explicit support of introspection is to allow other tools to make use of it and build abstractions around it instead of trying to reverse engineer. How the introspected format might look like is currently unknown. ### Validation Currently, to be able to do validation, one must create a new type that performs the validation step. This extra boilerplate is often a pain point. The idea is to instead allow for _optional_ validation via combinators using the derive macro.