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