xref: /aosp_15_r20/external/crosvm/serde_keyvalue/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! A lightweight serde deserializer for strings containing key-value pairs separated by commas, as
6*bb4ee6a4SAndroid Build Coastguard Worker //! commonly found in command-line parameters.
7*bb4ee6a4SAndroid Build Coastguard Worker //!
8*bb4ee6a4SAndroid Build Coastguard Worker //! Say your program takes a command-line option of the form:
9*bb4ee6a4SAndroid Build Coastguard Worker //!
10*bb4ee6a4SAndroid Build Coastguard Worker //! ```text
11*bb4ee6a4SAndroid Build Coastguard Worker //! --foo type=bar,active,nb_threads=8
12*bb4ee6a4SAndroid Build Coastguard Worker //! ```
13*bb4ee6a4SAndroid Build Coastguard Worker //!
14*bb4ee6a4SAndroid Build Coastguard Worker //! This crate provides a [from_key_values] function that deserializes these key-values into a
15*bb4ee6a4SAndroid Build Coastguard Worker //! configuration structure. Since it uses serde, the same configuration structure can also be
16*bb4ee6a4SAndroid Build Coastguard Worker //! created from any other supported source (such as a TOML or YAML configuration file) that uses
17*bb4ee6a4SAndroid Build Coastguard Worker //! the same keys.
18*bb4ee6a4SAndroid Build Coastguard Worker //!
19*bb4ee6a4SAndroid Build Coastguard Worker //! Integration with the [argh](https://github.com/google/argh) command-line parser is also
20*bb4ee6a4SAndroid Build Coastguard Worker //! provided via the `argh_derive` feature.
21*bb4ee6a4SAndroid Build Coastguard Worker //!
22*bb4ee6a4SAndroid Build Coastguard Worker //! The deserializer supports parsing signed and unsigned integers, booleans, strings (quoted or
23*bb4ee6a4SAndroid Build Coastguard Worker //! not), paths, and enums inside a top-level struct. The order in which the fields appear in the
24*bb4ee6a4SAndroid Build Coastguard Worker //! string is not important.
25*bb4ee6a4SAndroid Build Coastguard Worker //!
26*bb4ee6a4SAndroid Build Coastguard Worker //! Simple example:
27*bb4ee6a4SAndroid Build Coastguard Worker //!
28*bb4ee6a4SAndroid Build Coastguard Worker //! ```
29*bb4ee6a4SAndroid Build Coastguard Worker //! use serde_keyvalue::from_key_values;
30*bb4ee6a4SAndroid Build Coastguard Worker //! use serde::Deserialize;
31*bb4ee6a4SAndroid Build Coastguard Worker //!
32*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
33*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
34*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
35*bb4ee6a4SAndroid Build Coastguard Worker //!     threads: u8,
36*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
37*bb4ee6a4SAndroid Build Coastguard Worker //! }
38*bb4ee6a4SAndroid Build Coastguard Worker //!
39*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("path=/some/path,threads=16,active=true").unwrap();
40*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: "/some/path".into(), threads: 16, active: true });
41*bb4ee6a4SAndroid Build Coastguard Worker //!
42*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("threads=16,active=true,path=/some/path").unwrap();
43*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: "/some/path".into(), threads: 16, active: true });
44*bb4ee6a4SAndroid Build Coastguard Worker //! ```
45*bb4ee6a4SAndroid Build Coastguard Worker //!
46*bb4ee6a4SAndroid Build Coastguard Worker //! As a convenience the name of the first field of a struct can be omitted:
47*bb4ee6a4SAndroid Build Coastguard Worker //!
48*bb4ee6a4SAndroid Build Coastguard Worker //! ```
49*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
50*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
51*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
52*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
53*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
54*bb4ee6a4SAndroid Build Coastguard Worker //!     threads: u8,
55*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
56*bb4ee6a4SAndroid Build Coastguard Worker //! }
57*bb4ee6a4SAndroid Build Coastguard Worker //!
58*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("/some/path,threads=16,active=true").unwrap();
59*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: "/some/path".into(), threads: 16, active: true });
60*bb4ee6a4SAndroid Build Coastguard Worker //! ```
61*bb4ee6a4SAndroid Build Coastguard Worker //!
62*bb4ee6a4SAndroid Build Coastguard Worker //! Fields that are behind an `Option` can be omitted, in which case they will be `None`.
63*bb4ee6a4SAndroid Build Coastguard Worker //!
64*bb4ee6a4SAndroid Build Coastguard Worker //! ```
65*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
66*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
67*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
68*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
69*bb4ee6a4SAndroid Build Coastguard Worker //!     path: Option<String>,
70*bb4ee6a4SAndroid Build Coastguard Worker //!     threads: u8,
71*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
72*bb4ee6a4SAndroid Build Coastguard Worker //! }
73*bb4ee6a4SAndroid Build Coastguard Worker //!
74*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("path=/some/path,threads=16,active=true").unwrap();
75*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: Some("/some/path".into()), threads: 16, active: true });
76*bb4ee6a4SAndroid Build Coastguard Worker //!
77*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("threads=16,active=true").unwrap();
78*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: None, threads: 16, active: true });
79*bb4ee6a4SAndroid Build Coastguard Worker //! ```
80*bb4ee6a4SAndroid Build Coastguard Worker //!
81*bb4ee6a4SAndroid Build Coastguard Worker //! Alternatively, the serde `default` attribute can be used on select fields or on the whole
82*bb4ee6a4SAndroid Build Coastguard Worker //! struct to make unspecified fields be assigned their default value. In the following example only
83*bb4ee6a4SAndroid Build Coastguard Worker //! the `path` parameter must be specified.
84*bb4ee6a4SAndroid Build Coastguard Worker //!
85*bb4ee6a4SAndroid Build Coastguard Worker //! ```
86*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
87*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
88*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
89*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
90*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
91*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(default)]
92*bb4ee6a4SAndroid Build Coastguard Worker //!     threads: u8,
93*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(default)]
94*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
95*bb4ee6a4SAndroid Build Coastguard Worker //! }
96*bb4ee6a4SAndroid Build Coastguard Worker //!
97*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("path=/some/path").unwrap();
98*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: "/some/path".into(), threads: 0, active: false });
99*bb4ee6a4SAndroid Build Coastguard Worker //! ```
100*bb4ee6a4SAndroid Build Coastguard Worker //!
101*bb4ee6a4SAndroid Build Coastguard Worker //! A function providing a default value can also be specified, see the [serde documentation for
102*bb4ee6a4SAndroid Build Coastguard Worker //! field attributes](https://serde.rs/field-attrs.html) for details.
103*bb4ee6a4SAndroid Build Coastguard Worker //!
104*bb4ee6a4SAndroid Build Coastguard Worker //! Booleans can be `true` or `false`, or take no value at all, in which case they will be `true`.
105*bb4ee6a4SAndroid Build Coastguard Worker //! Combined with default values this allows to implement flags very easily:
106*bb4ee6a4SAndroid Build Coastguard Worker //!
107*bb4ee6a4SAndroid Build Coastguard Worker //! ```
108*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
109*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
110*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, Default, PartialEq, Deserialize)]
111*bb4ee6a4SAndroid Build Coastguard Worker //! #[serde(default)]
112*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
113*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
114*bb4ee6a4SAndroid Build Coastguard Worker //!     delayed: bool,
115*bb4ee6a4SAndroid Build Coastguard Worker //!     pooled: bool,
116*bb4ee6a4SAndroid Build Coastguard Worker //! }
117*bb4ee6a4SAndroid Build Coastguard Worker //!
118*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("active=true,delayed=false,pooled=true").unwrap();
119*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { active: true, delayed: false, pooled: true });
120*bb4ee6a4SAndroid Build Coastguard Worker //!
121*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("active,pooled").unwrap();
122*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { active: true, delayed: false, pooled: true });
123*bb4ee6a4SAndroid Build Coastguard Worker //! ```
124*bb4ee6a4SAndroid Build Coastguard Worker //!
125*bb4ee6a4SAndroid Build Coastguard Worker //! Strings can be quoted, which is useful if they need to include a comma or a bracket, which are
126*bb4ee6a4SAndroid Build Coastguard Worker //! considered separators for unquoted strings. Quoted strings can also contain escaped characters,
127*bb4ee6a4SAndroid Build Coastguard Worker //! where any character after a `\` is repeated as-is:
128*bb4ee6a4SAndroid Build Coastguard Worker //!
129*bb4ee6a4SAndroid Build Coastguard Worker //! ```
130*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
131*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
132*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
133*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
134*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
135*bb4ee6a4SAndroid Build Coastguard Worker //! }
136*bb4ee6a4SAndroid Build Coastguard Worker //!
137*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values(r#"path="/some/\"strange\"/pa,th""#).unwrap();
138*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { path: r#"/some/"strange"/pa,th"#.into() });
139*bb4ee6a4SAndroid Build Coastguard Worker //! ```
140*bb4ee6a4SAndroid Build Coastguard Worker //!
141*bb4ee6a4SAndroid Build Coastguard Worker //! Tuples and vectors are allowed and must be specified between `[` and `]`:
142*bb4ee6a4SAndroid Build Coastguard Worker //!
143*bb4ee6a4SAndroid Build Coastguard Worker //! ```
144*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
145*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
146*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
147*bb4ee6a4SAndroid Build Coastguard Worker //! struct Layout {
148*bb4ee6a4SAndroid Build Coastguard Worker //!     resolution: (u16, u16),
149*bb4ee6a4SAndroid Build Coastguard Worker //!     scanlines: Vec<u16>,
150*bb4ee6a4SAndroid Build Coastguard Worker //! }
151*bb4ee6a4SAndroid Build Coastguard Worker //!
152*bb4ee6a4SAndroid Build Coastguard Worker //! let layout: Layout = from_key_values("resolution=[320,200],scanlines=[0,64,128]").unwrap();
153*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(layout, Layout { resolution: (320, 200), scanlines: vec![0, 64, 128] });
154*bb4ee6a4SAndroid Build Coastguard Worker //! ```
155*bb4ee6a4SAndroid Build Coastguard Worker //!
156*bb4ee6a4SAndroid Build Coastguard Worker //! Enums can be directly specified by name. It is recommended to use the `rename_all` serde
157*bb4ee6a4SAndroid Build Coastguard Worker //! container attribute to make them parseable using snake or kebab case representation. Serde's
158*bb4ee6a4SAndroid Build Coastguard Worker //! `rename` and `alias` field attributes can also be used to provide shorter values:
159*bb4ee6a4SAndroid Build Coastguard Worker //!
160*bb4ee6a4SAndroid Build Coastguard Worker //! ```
161*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
162*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
163*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
164*bb4ee6a4SAndroid Build Coastguard Worker //! #[serde(rename_all="kebab-case")]
165*bb4ee6a4SAndroid Build Coastguard Worker //! enum Mode {
166*bb4ee6a4SAndroid Build Coastguard Worker //!     Slow,
167*bb4ee6a4SAndroid Build Coastguard Worker //!     Fast,
168*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(rename="ludicrous")]
169*bb4ee6a4SAndroid Build Coastguard Worker //!     LudicrousSpeed,
170*bb4ee6a4SAndroid Build Coastguard Worker //! }
171*bb4ee6a4SAndroid Build Coastguard Worker //!
172*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Deserialize, PartialEq, Debug)]
173*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
174*bb4ee6a4SAndroid Build Coastguard Worker //!     mode: Mode,
175*bb4ee6a4SAndroid Build Coastguard Worker //! }
176*bb4ee6a4SAndroid Build Coastguard Worker //!
177*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("mode=slow").unwrap();
178*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::Slow });
179*bb4ee6a4SAndroid Build Coastguard Worker //!
180*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("mode=ludicrous").unwrap();
181*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::LudicrousSpeed });
182*bb4ee6a4SAndroid Build Coastguard Worker //! ```
183*bb4ee6a4SAndroid Build Coastguard Worker //!
184*bb4ee6a4SAndroid Build Coastguard Worker //! A nice use of enums is along with sets, where it allows to e.g. easily specify flags:
185*bb4ee6a4SAndroid Build Coastguard Worker //!
186*bb4ee6a4SAndroid Build Coastguard Worker //! ```
187*bb4ee6a4SAndroid Build Coastguard Worker //! # use std::collections::BTreeSet;
188*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
189*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
190*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Deserialize, PartialEq, Eq, Debug, PartialOrd, Ord)]
191*bb4ee6a4SAndroid Build Coastguard Worker //! #[serde(rename_all = "kebab-case")]
192*bb4ee6a4SAndroid Build Coastguard Worker //! enum Flags {
193*bb4ee6a4SAndroid Build Coastguard Worker //!     Awesome,
194*bb4ee6a4SAndroid Build Coastguard Worker //!     Fluffy,
195*bb4ee6a4SAndroid Build Coastguard Worker //!     Transparent,
196*bb4ee6a4SAndroid Build Coastguard Worker //! }
197*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Deserialize, PartialEq, Debug)]
198*bb4ee6a4SAndroid Build Coastguard Worker //! struct TestStruct {
199*bb4ee6a4SAndroid Build Coastguard Worker //!     flags: BTreeSet<Flags>,
200*bb4ee6a4SAndroid Build Coastguard Worker //! }
201*bb4ee6a4SAndroid Build Coastguard Worker //!
202*bb4ee6a4SAndroid Build Coastguard Worker //! let res: TestStruct = from_key_values("flags=[awesome,fluffy]").unwrap();
203*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(
204*bb4ee6a4SAndroid Build Coastguard Worker //!     res,
205*bb4ee6a4SAndroid Build Coastguard Worker //!     TestStruct {
206*bb4ee6a4SAndroid Build Coastguard Worker //!         flags: BTreeSet::from([Flags::Awesome, Flags::Fluffy]),
207*bb4ee6a4SAndroid Build Coastguard Worker //!     }
208*bb4ee6a4SAndroid Build Coastguard Worker //! );
209*bb4ee6a4SAndroid Build Coastguard Worker //! ```
210*bb4ee6a4SAndroid Build Coastguard Worker //!
211*bb4ee6a4SAndroid Build Coastguard Worker //! Enums taking a single value can use the `flatten` field attribute in order to be inferred from
212*bb4ee6a4SAndroid Build Coastguard Worker //! their variant key directly:
213*bb4ee6a4SAndroid Build Coastguard Worker //!
214*bb4ee6a4SAndroid Build Coastguard Worker //! ```
215*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
216*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
217*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
218*bb4ee6a4SAndroid Build Coastguard Worker //! #[serde(rename_all="kebab-case")]
219*bb4ee6a4SAndroid Build Coastguard Worker //! enum Mode {
220*bb4ee6a4SAndroid Build Coastguard Worker //!     // Work with a local file.
221*bb4ee6a4SAndroid Build Coastguard Worker //!     File(String),
222*bb4ee6a4SAndroid Build Coastguard Worker //!     // Work with a remote URL.
223*bb4ee6a4SAndroid Build Coastguard Worker //!     Url(String),
224*bb4ee6a4SAndroid Build Coastguard Worker //! }
225*bb4ee6a4SAndroid Build Coastguard Worker //!
226*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Deserialize, PartialEq, Debug)]
227*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
228*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(flatten)]
229*bb4ee6a4SAndroid Build Coastguard Worker //!     mode: Mode,
230*bb4ee6a4SAndroid Build Coastguard Worker //! }
231*bb4ee6a4SAndroid Build Coastguard Worker //!
232*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("file=/some/path").unwrap();
233*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::File("/some/path".into()) });
234*bb4ee6a4SAndroid Build Coastguard Worker //!
235*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("url=https://www.google.com").unwrap();
236*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::Url("https://www.google.com".into()) });
237*bb4ee6a4SAndroid Build Coastguard Worker //! ```
238*bb4ee6a4SAndroid Build Coastguard Worker //!
239*bb4ee6a4SAndroid Build Coastguard Worker //! The `flatten` attribute can also be used to embed one struct within another one and parse both
240*bb4ee6a4SAndroid Build Coastguard Worker //! from the same string:
241*bb4ee6a4SAndroid Build Coastguard Worker //!
242*bb4ee6a4SAndroid Build Coastguard Worker //! ```
243*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
244*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
245*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
246*bb4ee6a4SAndroid Build Coastguard Worker //! struct BaseConfig {
247*bb4ee6a4SAndroid Build Coastguard Worker //!     enabled: bool,
248*bb4ee6a4SAndroid Build Coastguard Worker //!     num_threads: u8,
249*bb4ee6a4SAndroid Build Coastguard Worker //! }
250*bb4ee6a4SAndroid Build Coastguard Worker //!
251*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
252*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
253*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(flatten)]
254*bb4ee6a4SAndroid Build Coastguard Worker //!     base: BaseConfig,
255*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
256*bb4ee6a4SAndroid Build Coastguard Worker //! }
257*bb4ee6a4SAndroid Build Coastguard Worker //!
258*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("path=/some/path,enabled,num_threads=16").unwrap();
259*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(
260*bb4ee6a4SAndroid Build Coastguard Worker //!     config,
261*bb4ee6a4SAndroid Build Coastguard Worker //!     Config {
262*bb4ee6a4SAndroid Build Coastguard Worker //!         path: "/some/path".into(),
263*bb4ee6a4SAndroid Build Coastguard Worker //!         base: BaseConfig {
264*bb4ee6a4SAndroid Build Coastguard Worker //!             num_threads: 16,
265*bb4ee6a4SAndroid Build Coastguard Worker //!             enabled: true,
266*bb4ee6a4SAndroid Build Coastguard Worker //!         }
267*bb4ee6a4SAndroid Build Coastguard Worker //!     }
268*bb4ee6a4SAndroid Build Coastguard Worker //! );
269*bb4ee6a4SAndroid Build Coastguard Worker //! ```
270*bb4ee6a4SAndroid Build Coastguard Worker //!
271*bb4ee6a4SAndroid Build Coastguard Worker //! If an enum's variants are made of structs, it can take the `untagged` container attribute to be
272*bb4ee6a4SAndroid Build Coastguard Worker //! inferred directly from the fields of the embedded structs:
273*bb4ee6a4SAndroid Build Coastguard Worker //!
274*bb4ee6a4SAndroid Build Coastguard Worker //! ```
275*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
276*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
277*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
278*bb4ee6a4SAndroid Build Coastguard Worker //! #[serde(untagged)]
279*bb4ee6a4SAndroid Build Coastguard Worker //! enum Mode {
280*bb4ee6a4SAndroid Build Coastguard Worker //!     // Work with a local file.
281*bb4ee6a4SAndroid Build Coastguard Worker //!     File {
282*bb4ee6a4SAndroid Build Coastguard Worker //!         path: String,
283*bb4ee6a4SAndroid Build Coastguard Worker //!         #[serde(default)]
284*bb4ee6a4SAndroid Build Coastguard Worker //!         read_only: bool,
285*bb4ee6a4SAndroid Build Coastguard Worker //!     },
286*bb4ee6a4SAndroid Build Coastguard Worker //!     // Work with a remote URL.
287*bb4ee6a4SAndroid Build Coastguard Worker //!     Remote {
288*bb4ee6a4SAndroid Build Coastguard Worker //!         server: String,
289*bb4ee6a4SAndroid Build Coastguard Worker //!         port: u16,
290*bb4ee6a4SAndroid Build Coastguard Worker //!     }
291*bb4ee6a4SAndroid Build Coastguard Worker //! }
292*bb4ee6a4SAndroid Build Coastguard Worker //!
293*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
294*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
295*bb4ee6a4SAndroid Build Coastguard Worker //!     #[serde(flatten)]
296*bb4ee6a4SAndroid Build Coastguard Worker //!     mode: Mode,
297*bb4ee6a4SAndroid Build Coastguard Worker //! }
298*bb4ee6a4SAndroid Build Coastguard Worker //!
299*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("path=/some/path").unwrap();
300*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::File { path: "/some/path".into(), read_only: false } });
301*bb4ee6a4SAndroid Build Coastguard Worker //!
302*bb4ee6a4SAndroid Build Coastguard Worker //! let config: Config = from_key_values("server=google.com,port=80").unwrap();
303*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(config, Config { mode: Mode::Remote { server: "google.com".into(), port: 80 } });
304*bb4ee6a4SAndroid Build Coastguard Worker //! ```
305*bb4ee6a4SAndroid Build Coastguard Worker //!
306*bb4ee6a4SAndroid Build Coastguard Worker //! Using this crate, parsing errors and invalid or missing fields are precisely reported:
307*bb4ee6a4SAndroid Build Coastguard Worker //!
308*bb4ee6a4SAndroid Build Coastguard Worker //! ```
309*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde_keyvalue::from_key_values;
310*bb4ee6a4SAndroid Build Coastguard Worker //! # use serde::Deserialize;
311*bb4ee6a4SAndroid Build Coastguard Worker //! #[derive(Debug, PartialEq, Deserialize)]
312*bb4ee6a4SAndroid Build Coastguard Worker //! struct Config {
313*bb4ee6a4SAndroid Build Coastguard Worker //!     path: String,
314*bb4ee6a4SAndroid Build Coastguard Worker //!     threads: u8,
315*bb4ee6a4SAndroid Build Coastguard Worker //!     active: bool,
316*bb4ee6a4SAndroid Build Coastguard Worker //! }
317*bb4ee6a4SAndroid Build Coastguard Worker //!
318*bb4ee6a4SAndroid Build Coastguard Worker //! let config = from_key_values::<Config>("path=/some/path,active=true").unwrap_err();
319*bb4ee6a4SAndroid Build Coastguard Worker //! assert_eq!(format!("{}", config), "missing field `threads`");
320*bb4ee6a4SAndroid Build Coastguard Worker //! ```
321*bb4ee6a4SAndroid Build Coastguard Worker //!
322*bb4ee6a4SAndroid Build Coastguard Worker //! Most of the serde [container](https://serde.rs/container-attrs.html) and
323*bb4ee6a4SAndroid Build Coastguard Worker //! [field](https://serde.rs/field-attrs.html) attributes can be applied to your configuration
324*bb4ee6a4SAndroid Build Coastguard Worker //! struct. Most useful ones include
325*bb4ee6a4SAndroid Build Coastguard Worker //! [`deny_unknown_fields`](https://serde.rs/container-attrs.html#deny_unknown_fields) to report an
326*bb4ee6a4SAndroid Build Coastguard Worker //! error if an unknown field is met in the input, and
327*bb4ee6a4SAndroid Build Coastguard Worker //! [`deserialize_with`](https://serde.rs/field-attrs.html#deserialize_with) to use a custom
328*bb4ee6a4SAndroid Build Coastguard Worker //! deserialization function for a specific field.
329*bb4ee6a4SAndroid Build Coastguard Worker //!
330*bb4ee6a4SAndroid Build Coastguard Worker //! Be aware that the use of `flatten` comes with some severe limitations. Because type information
331*bb4ee6a4SAndroid Build Coastguard Worker //! is not available to the deserializer, it will try to determine the type of fields using the
332*bb4ee6a4SAndroid Build Coastguard Worker //! input as its sole hint. For instance, any number will be returned as an integer type, and if the
333*bb4ee6a4SAndroid Build Coastguard Worker //! parsed structure was actually expecting a number as a string, then an error will occur.
334*bb4ee6a4SAndroid Build Coastguard Worker //! Struct enums also cannot be flattened and won't be recognized at all.
335*bb4ee6a4SAndroid Build Coastguard Worker //!
336*bb4ee6a4SAndroid Build Coastguard Worker //! For these reasons, it is discouraged to use `flatten` except when neither the embedding not the
337*bb4ee6a4SAndroid Build Coastguard Worker //! flattened structs has a member of string type.
338*bb4ee6a4SAndroid Build Coastguard Worker //!
339*bb4ee6a4SAndroid Build Coastguard Worker //! Most of the time, similar functionality can be obtained by implementing a custom deserializer
340*bb4ee6a4SAndroid Build Coastguard Worker //! that parses the embedding struct's member and then the flattened struct in a specific order
341*bb4ee6a4SAndroid Build Coastguard Worker //! using the [`key_values::KeyValueDeserializer`] interface directly.
342*bb4ee6a4SAndroid Build Coastguard Worker //!
343*bb4ee6a4SAndroid Build Coastguard Worker //! Another limitation of using `flatten` that is inherent to serde is that it won't allow
344*bb4ee6a4SAndroid Build Coastguard Worker //! `deny_unknown_fields` to be used in either the embedding or the flattened struct.
345*bb4ee6a4SAndroid Build Coastguard Worker #![deny(missing_docs)]
346*bb4ee6a4SAndroid Build Coastguard Worker 
347*bb4ee6a4SAndroid Build Coastguard Worker mod key_values;
348*bb4ee6a4SAndroid Build Coastguard Worker 
349*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "argh_derive")]
350*bb4ee6a4SAndroid Build Coastguard Worker pub use argh;
351*bb4ee6a4SAndroid Build Coastguard Worker pub use key_values::from_key_values;
352*bb4ee6a4SAndroid Build Coastguard Worker pub use key_values::ErrorKind;
353*bb4ee6a4SAndroid Build Coastguard Worker pub use key_values::KeyValueDeserializer;
354*bb4ee6a4SAndroid Build Coastguard Worker pub use key_values::ParseError;
355*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(feature = "argh_derive")]
356*bb4ee6a4SAndroid Build Coastguard Worker pub use serde_keyvalue_derive::FromKeyValues;
357