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
113
114
115
116
117
//! Use a serde Deserializer as a value-soure for layered config.
//!
//! We discussed extensively in MOTIVATION.md why serde *deserializer* libraries
//! are unable to cause serde to return multiple errors when deserialization fails.
//! We also discussed why the ability to do that is so important to us.
//!
//! In this module, we create a way that we *can* return multiple errors when walking the
//! `Deserializer`, as long as we control the `Deserialize` side of things.
//! Since we control the proc-macro here, we have such control.
//!
//! We can even incorporate arbitrary other data, like a `ConfContext`, into the walk,
//! and use other value sources besides the `Deserializer` when initializing the struct.
//!
//! The main idea is to use the `DeserializeSeed` trait from `serde`, implement it
//! on (a variation of) ConfContext, and set `type Value = Result<S, Vec<InnerError>>;`
//! This means that when we implement `DeseralizeSeed::deserialize`, we get all the `ConfContext`,
//! meaning parsed args, env, any thing else that we want, and we get the `Deserializer`.
//! The type we have to return is `Result<Result<S, Vec<InnerError>>, D::Error>`, but if
//! we simply always return the `Ok` variant, then we basically have exactly the signature that
//! we wanted to implement.
//!
//! So we can inspect all this data, walk the deserializer and see what it has, then walk the
//! fields it didn't have any data for, and initialize everything according to the hierachical
//! config priority. We can collect as many errors as we need to, and when we return, we always
//! return `Ok(Ok(instance))` or `Ok(Err(errors))`. We can also use this same `DeserializeSeed`
//! trick when we recurse into a substructure, because `serde::de::MapAccess` allows us to invoke
//! the `DeserializeSeed` API however we want when recursing.
//!
//! At the end, the `ConfBuilder` is able to manage the error trickery we performed here and
//! return what the user was expecting, so none of this has any negative impact on the public API.
//!
//! And the plus side is, we have only loose coupling with all the file-reading and format-parsing
//! code. Users can bring their own stuff, whatever `serde_json` or `serde_json_lenient` or any
//! other variation, and configure it however they want. And conf has no explicit dependencies on
//! anything that reads a file.
//!
//! ***
//!
//! Note that due to this technique, you will get better error reporting from
//! using `Conf` to deserialize a large structure from a `serde::Deserializer` than
//! `serde::Deserialize` would be able to (!), because we can collect errors from all over the
//! structure.
//!
//! However, the technique has some limitations -- it has to be acceptable to type-erase
//! the serde errors. In this crate, we are mapping all the errors that can occur to
//! `conf::InnerError` and then aggregating them as one `clap::Error` essentially. If you wanted to
//! generalize the technique, and you don't want to break compat with existing serde Deserializers,
//! you would need to use `Box<dyn Error>` or something, but `serde` doesn't give you `'static` on
//! the error types, or convertibility between e.g. Deserializer errors and MapAccess errors.
//!
//! Another thing is, this is only really a good technique when you are deserializing a
//! self-describing format, like json, yaml, toml, etc, and you deserialize their type from
//! bytes-on-the-wire first. If you try to pass e.g. `serde_json::Deserializer::from_reader` as the
//! `Deserializer` here, you may get worse error reporting if the file is not well-formed
//! potentially, because once you have junk bytes on the wire and the wire protocol isn't working
//! right, trying to read more things is probably futile and just creates noise.
//!
//! Another drawback of this approach is, because we aren't using the `serde::Deserialize` derive
//! macro from `serde_derive`, we don't automatically get support for any of the `#[serde(...)]`
//! annotations. However, this is also a plus in some sense, because for things like
//! `#[serde(flatten)]`, we don't inherit any of the limitations or bugs, and we can choose to
//! implement it in a way that makes sense for our use-case later.
pub use ConfSerdeBuilder;
pub use ;
pub use ;
/// Helper for deserializing serde MapAccess keys in a struct visitor.
/// Similar to `String`, but uses "Deserializer::deserializer_identifier" to hint.