err-per-field
This crate provides a more fine-grained control over field-level error handling.
When we are writing a huge complex project, we may gather a lot of information into a single struct, such as:
However, we may encounter Results or Options when generating such subinfos. The core logic is to deal with GatheredInformation if all subinfos are retrieved successfully. If some fields are failed to retrieve, let's say info2, we should inform the user that failure, and may provide other successfully retrieved infos (such as info1 and info3) to user.
To handle this gracefully, dynamically-typed languages such as Javascript can be rather straight forward:
const gathered_information = ;
if else
For Rust, a statically-typed langugage, things become complicated:
let gathered_information_wrapper = gather_information;
if let GatheredInformationWrapper = gathered_information_wrappper else
We must:
- Define a wrapper struct of
GatheredInformation, whose fields areResults orOptions according to the generating functions; - Build the real
GatheredInformationafter validation checks; - Pass the real gathered information to the downstream functions.
This is a boring process and the extra wrapper structs are very distracting.
To handle this problem, this crate provides a rather simple and graceful method.
Example
use ;
;
// If we write this in another file, we can simply import `Wrapper` type
// from `err_per_field` crate.
let result: = generate_foo.try_into;
assert!;
match result
By using this crate, we don't need to write wrapper structs by our own, and we can focus on the core logic of our product. All we need to do is:
- Derive the core struct
Foowith macroErrPerFieldand mark fields which may beResults orOptions with attributes; - When generating the struct
Foo, useWrapper<Foo>as return type. This wrapper struct has the same field names withFoo, and their types may beResults orOptions as we mark when declaration; - When using the generated struct, we can validate its fields by calling
try_into, and if it is valid, the result isOkand the inner type isFoo, and we can directly deal with it as the core logic; if it is invalid, the result isErrand the inner type isFoo's wrapper, and we can check each fields for error handling.
Usage
When we use #[derive(ErrPerField)] on a struct Foo, the macro generates a wrapper struct and we can use Wrapper<Foo> to access it. This wrapper struct has the same field names as Foo, and for a field bar, if foo.bar has type Bar, then foo_wrapper.bar's type is:
Result<Bar, AnError>if there is a field-level attribute#[err_per_field(maybe_error = "AnError")];Option<Bar>if there is a field-level attribute#[err_per_field(maybe_none)];Barif otherwise.
Foo will automatically implements TryFrom<Wrapper<Foo>>. If there is a field being Err or None, the conversion fails and the result is Err, the inner value is foo's wrapper itself without any change; otherwise the conversion succeeds and the result is Ok, the inner type is the final foo, with all fields extracted from Results and Options.