1 unstable release
0.0.0-reserved | Feb 16, 2023 |
---|
#615 in #deserialize
14KB
deer
deer
is an experimental backend-agnostic deserialization framework for Rust, featuring meaningful error messages and context (utilizing 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:
#[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::<Body>(payload);
let error = result.expect_err("should fail");
println!("{error:?}");
let result = serde_json::from_value::<Body>(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.
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.
What it may look like
#[derive(deer::Deserialize)]
struct Payload {
#[validate(all(min(12), max(24)))]
length: u8
}
Lax Deserialization
Instead of strictly deserializing types, one might prefer to deserialize while coercing values ("1"
might be interpreted as 1
instead). This behavior would be opt-in instead of opt-out and enabled on the derive
level.
Deserializer with Parser
deer
currently relies on external tools (notably serde
) to implement parsing of different formats, which means that we still fail fast during the parsing of malformed input. In the future, deer
might provide a parser that tries to recover from parsing errors and provide meaningful diagnostics.
What it may look like
{
"i8": "string"
Level of "deer
"
deer
strives to be as composable and configurable as possible, which means that all non-core behavior should be opt-in, and the speed penalty of bare deer
should be minimal. In the future, we might want to provide additional configuration parameters (maximum depth, the maximum number of errors, etc.) to allow for further tweaking in all use cases where speed is of utmost importance.
Contributors
deer
was created by Bilal Mahmoud. It is being developed in conjunction with HASH. As an open-source project, we gratefully accept external contributions and have published a contributing guide that outlines the process. If you have questions, please reach out to us on our Discord server. You can also report bugs directly on the GitHub repo.
License
deer
is available under a number of different open-source licenses. Please see the LICENSE file to review your options.