Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions examples/demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ edition = "2021"
publish = false

[dependencies]
more-config = { path = "../../src", features = ["mem", "env", "cmd", "json", "binder"] }
serde = { version = "1.0", features = ["derive"] }
more-config = { path = "../../src", features = ["mem", "env", "cmd", "json", "binder", "struct"] }
serde = { version = "1.0", features = ["derive"] }
serde-intermediate = "1.6"
30 changes: 26 additions & 4 deletions examples/demo/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
use config::{*, ext::*};
use serde::Deserialize;
use serde::{Serialize, Deserialize};

#[allow(dead_code)]
#[derive(Default, Deserialize)]
#[derive(Default, Serialize, Deserialize, Clone)]
#[serde(rename_all(serialize = "PascalCase"))]
#[serde(rename_all(deserialize = "PascalCase"))]
struct Client {
region: String,
url: String,
}

#[allow(dead_code)]
#[derive(Default, Deserialize)]
#[derive(Default, Serialize, Deserialize, Clone)]
#[serde(rename_all(serialize = "PascalCase"))]
#[serde(rename_all(deserialize = "PascalCase"))]
struct SubOptions {
value: i32,
}

#[allow(dead_code)]
#[derive(Default, Serialize, Deserialize, Clone)]
#[serde(rename_all(serialize = "PascalCase"))]
#[serde(rename_all(deserialize = "PascalCase"))]
struct AppOptions {
text: String,
demo: bool,
sub: SubOptions,
clients: Vec<Client>,
}

Expand All @@ -24,7 +35,16 @@ fn main() {
.parent()
.unwrap()
.join("../../examples/demo/demo.json");

let default = AppOptions {
text: String::from("Default text"),
demo: false,
sub: SubOptions { value: 34},
clients: Vec::new(),
};

let config = DefaultConfigurationBuilder::new()
.add_struct(default.clone())
.add_in_memory(&[("Demo", "false")])
.add_json_file(file)
.add_env_vars()
Expand All @@ -36,8 +56,10 @@ fn main() {
if app.demo {
println!("{}", &app.text);
println!("{}", &app.clients[0].region);
println!("Suboption value by query: {}", config.get("Sub:Value").unwrap().as_str());
println!("Suboption value by binding: {}", &app.sub.value);
return;
}

println!("Not a demo!");
}
}
3 changes: 2 additions & 1 deletion guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
- [Working With Data](guide/data.md)
- [File Sources](guide/files.md)
- [In-Memory Provider](guide/memory.md)
- [Data Structure Provider](guide/struct.md)
- [Environment Variable Provider](guide/env.md)
- [Command-Line Provider](guide/cmd.md)
- [JSON Provider](guide/json.md)
- [XML Provider](guide/xml.md)
- [INI Provider](guide/ini.md)
- [Chained Provider](guide/chained.md)
- [Data Binding](guide/binding.md)
- [Data Binding](guide/binding.md)
135 changes: 135 additions & 0 deletions guide/src/guide/struct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
{{#include links.md}}

# Data-Structure Configuration Provider

>These features are only available if the **struct** feature is activated

The [`StructConfigurationProvider`] uses an in-memory data structure as configuration key-value pairs. This is most useful as an alternative default configuration to in-memory configuration or when providing test values.

The following code adds a data structure collection to the configuration system and displays the settings:

```rust
use config::{*, ext::*};
use serde::Serialize;

#[derive(Default, Serialize, Clone)]
#[serde(rename_all(serialize = "PascalCase"))]
struct PerfSettings {
cores: u8,
}

#[derive(Default, Serialize, Clone)]
#[serde(rename_all(serialize = "PascalCase"))]
struct AppOptions {
title: String,
perf: PerfSettings,
}

fn main() {
let default = AppOptions {
title: String::from("Banana processor"),
perf: SubOptions{ cores: 7 },
};

let config = DefaultConfigurationBuilder::new()
.add_struct(default.clone())
.build()
.unwrap();

let title = config.get("title").unwrap().as_str();
let cores = config.get("Perf:Cores").unwrap().as_str();

println!("Title: {}\n\
Cores: {}\n\
title,
cores);
}
```

Instead of a data structure, one can also load a tuple, a vector, or a map.
Values from Tuples and Vectors can be retrieved with their index as key:

```rust
use config::{*, ext::*};

fn main() {
let value = std::vec::Vec::from([32, 56]);
let config = DefaultConfigurationBuilder::new()
.add_struct(value)
.build()
.unwrap();
println!("[{}, {}]", config.get("0"), config.get("1"));
}
```

Or, same example with a tuple:

```rust
use config::{*, ext::*};

fn main() {
let value = (32, 56);
let config = DefaultConfigurationBuilder::new()
.add_struct(value)
.build()
.unwrap();
println!("[{}, {}]", config.get("0"), config.get("1"));
}
```

When loading maps, it is best to restrict to maps using strings as keys. When using strings as keys, it is possible to bind the data to some data structure:

```rust
use config::{*, ext::*};

#[derive(Deserialize)]
#[serde(rename_all(deserialize = "PascalCase"))]
struct FooBar {
foo: String,
karoucho: i32,
}

fn main() {
let mut value: HashMap<&str, &str> = HashMap::new();
value.insert("foo", "bar");
value.insert("karoucho", "34");

let config = DefaultConfigurationBuilder::new()
.add_struct(value)
.build()
.unwrap();
let foo = config.get("foo");
println!("foo by query: {}", foo);
let karoucho = config.get("karoucho");
println!("karoucho by query: {}", karoucho);

let options: FooBar = config.reify();
println!("foo by binding: {}", options.foo);
println!("karoucho by binding: {}", options.karoucho);
}
```

When using non-strings as keys, `serde` appends type information to the key name. The suffix must be then given when querying the value:


```rust
use config::{*, ext::*};

fn main() {
let mut value: HashMap<i32, i32> = HashMap::new();
value.insert(-32, 56);
let config = DefaultConfigurationBuilder::new()
.add_struct(value)
.build()
.unwrap();
let loaded = config.get("-32_i32");
assert_eq!(loaded.unwrap().as_str(), "56");
}

```

Also, note that binding is then not possible, since a data structure member name cannot start with a digit or a sign.
`bool` keys are not added any type suffix to the generated key, and can be used to bind into a data structure.
However, this can be applied in a limited number of scenarios and using non-string as keys is generally not supported.


6 changes: 4 additions & 2 deletions src/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ include = ["*.rs", "!build.rs", "README.md"]

# RUSTDOCFLAGS="--cfg docsrs"; cargo +nightly doc
[package.metadata.docs.rs]
features = ["std", "chained", "mem", "env", "cmd", "ini", "json", "xml", "binder"]
features = ["std", "chained", "mem", "env", "cmd", "ini", "json", "xml", "binder", "struct"]
rustdoc-args = ["--cfg", "docsrs"]

[lib]
Expand All @@ -34,6 +34,7 @@ ini = ["util", "configparser", "more-changetoken/fs"]
binder = ["serde"]
json = ["util", "serde_json", "more-changetoken/fs"]
xml = ["util", "xml_rs", "more-changetoken/fs"]
struct = ["serde-intermediate"]

[dependencies]
more-changetoken = "2.0"
Expand All @@ -42,10 +43,11 @@ serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
xml_rs = { version = "0.8", package = "xml", optional = true }
cfg-if = "1.0"
serde-intermediate = { version = "1.6", optional = true }

[dev-dependencies]
test-case = "2.2"

[dev-dependencies.more-config]
path = "."
features = ["cmd"]
features = ["cmd"]
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ mod binder;
#[cfg(feature = "binder")]
mod de;

#[cfg(feature = "struct")]
mod r#struct;

mod file;
pub use builder::*;
pub use configuration::*;
Expand Down Expand Up @@ -97,6 +100,10 @@ pub use cmd::{CommandLineConfigurationProvider, CommandLineConfigurationSource};
#[cfg_attr(docsrs, doc(cfg(feature = "xml")))]
pub use xml::{XmlConfigurationProvider, XmlConfigurationSource};

#[cfg(feature = "struct")]
#[cfg_attr(docsrs, doc(cfg(feature = "struct")))]
pub use r#struct::{StructConfigurationProvider, StructConfigurationSource};

/// Contains configuration extension methods.
pub mod ext {

Expand Down Expand Up @@ -138,6 +145,10 @@ pub mod ext {
#[cfg_attr(docsrs, doc(cfg(feature = "binder")))]
pub use de::*;

#[cfg(feature = "struct")]
#[cfg_attr(docsrs, doc(cfg(feature = "struct")))]
pub use r#struct::ext::*;

pub use section::ext::*;
pub use file::ext::*;
}
Loading