1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
mod utils;
use {
anstream::println,
compris::{annotate::*, parse::*, resolve::*, *},
depiction::*,
};
// Note that #[derive(Resolve)] requires an implementation of the Default trait,
// which we here get from #[derive(Default)]
#[derive(Debug, Default, Resolve)]
// To avoid compiler warnings
#[allow(dead_code)]
struct User {
// "required" means we will get an error if the key is not present in the map
// "single" means that we can resolve on *just* this field as a shorter notation
// (only one field can be marked as "single")
#[resolve(required, single)]
name: String,
// Not "required", so will have the default value if the key is not present in the map
#[resolve]
credit: i32,
// We can explicitly set the map key; otherwise it is the field name
#[resolve(required, key = "enabled")]
is_enabled: bool,
// Normally Variant::Null will cause an error when resolved
// (Null can only be resolved into Null; why would you want a struct field that is Null?)
// But we can explicitly add support for Null
// There are two options:
// Either `resolve(null = ...)` with the value assigned to null (we do this here)
// Or `resolve(ignore_null)`, which will leave the field at the default value
#[resolve(null = Some("no group".into()))]
group: Option<String>,
// Fields without #[resolve] will be ignored and just have the default value
ignored: bool,
}
pub fn main() {
// See examples/literal.rs
let variant =
without_annotations!(normal_map![("name", "Tal"), ("credit", 800), ("enabled", true), ("group", "moderators")]);
// Simplest! Resolve the variant into our struct
// ("fail fast" means that we will fail on the first error and return it)
let user: User = variant.resolve().expect("resolve");
utils::heading("resolved", true);
println!("{:#?}", user);
// Actually, this is even simpler:
// We can resolve for just the field tagged as "single" (in this case it's the `name` field)
// (a.k.a. "short notation")
let variant = without_annotations!(normal!("Tal"));
let user: User = variant.resolve().expect("resolve");
utils::heading("resolved (single)", false);
println!("{:#?}", user);
// Now let's intentionally cause errors by not specifying required fields, using wrong value
// types, and specifying unsupported keys
// We'll also parse JSON first in order to demonstrate annotations for the error message
// (there's are no annotations for the literal values above)
let json = r#"[{
"name": "Tal",
"credit": "wrong type",
"group": null
}, {
"name": "Shiri",
"credit": 123,
"enabled": true,
"mystery key 1!": 456,
"mystery key 2!": null
}]"#;
let variant =
with_annotations!(Parser::new(Format::JSON).with_try_integers(true).parse_string(json).expect("parse"));
// Note that we can resolve directly into Vecs (and HashMaps, too)
let result: Result<Vec<User>, _> = variant.clone().resolve();
utils::heading("fail-fast error", false);
result.err().expect("error").annotated_depiction().print_default_depiction();
// Instead of failing fast, we can call "resolve_with_errors" to accumulate all the errors *without* failing
// Note that we might still get a partially-resolved result even when there are accumulated errors,
// but that behavior depends on the resolver implementations
let mut errors = ResolveErrors::default();
let users: Vec<User> =
variant.resolve_with_errors(&mut errors).expect("errors should be accumulated").expect("some");
utils::heading("partially resolved", false);
println!("{:#?}", users);
if !errors.is_empty() {
println!();
errors.annotated_depictions(Some("accumulated errors".into())).print_default_depiction();
}
// Continue to examples/resolve_advanced.rs to learn more
}