pub struct Directory<F> { /* private fields */ }Expand description
A Provider that sources values from a (possibly nested) directory of files in a given
Format.
§Constructing
A Directory provider is typically constructed indirectly via a type that
implements the Format trait via the FormatExt::directory() method which in-turn defers
Directory::new() by default:
// The `FormatExt` trait must be in-scope to use its methods.
use figment::providers::{Format, Toml};
use figment_directory::{Directory, FormatExt as _};
// These two are equivalent, except the former requires the explicit type.
let json_directory = Directory::<Toml>::new("foo");
let json_directory = Toml::directory("foo");§Provider Details
-
Profile
This provider does not set a profile.
-
Metadata
This provider is named
${NAME} directory, where${NAME}isFormat::NAME. The directories file’s paths are specified as fileSource. Path interpolation is unchanged from the default. -
Data (Unnested, default)
When nesting is not specified, the source files in the given directory are read and parsed, and the parsed dictionary is emitted into the profile configurable via
Directory::profile(), which defaults toProfile::Default. If the source dictionary is not present an empty dictionary is emitted. -
Data (Nested)
When nesting is specified, the directory is expected to contain files and or subdirectories named after your profiles. These subdirectories and files are parsed and emitted into the corresponding profiles.
/root /root/default.toml | /root/default/foo.toml |– these get put into the “default” profile /root/default/bar.toml |
/root/development.toml | /root/development/foo.toml | – these get put into the “development” profile
-
Conflict Resolution Per default, values in files that are higher up in the directory tree override values in deeply nested files. As an example, take these two files:
# /root/a.toml [b] c = 1# /root/a/b.toml c = 2The provider will prefer the value in
a.toml, since it is higher up thana/b.toml. Therefore,c = 1will “win”.This strategy corresponds to the “Join” strategy in the figment docs on conflict resolution. The behaviour can be changed by using the methods on
Directorycorresponding to the available strategies:Directory::merge,Directory::adjoin,Directory::admergeandDirectory::join(if you like to be explicit).
Implementations§
Source§impl<F> Directory<F>
impl<F> Directory<F>
pub fn new<P: Into<PathBuf>>(path: P) -> Self
Sourcepub fn nested(self) -> Self
pub fn nested(self) -> Self
Enables nesting on self, which results in top-level keys of the
sourced data being treated as profiles.
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config {
numbers: Vec<usize>,
untyped: Map<String, usize>,
}
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/default.toml", r#"
[untyped]
global = 0
hi = 7
"#)?;
jail.create_file("cfg/staging.toml", r#"
numbers = [1, 2, 3]
"#)?;
jail.create_file("cfg/release.toml", r#"
numbers = [6, 7, 8]
"#)?;
// Enable nesting via `nested()`.
let figment = Figment::from(Toml::directory("cfg").nested());
let figment = figment.select("staging");
let config: Config = figment.extract()?;
assert_eq!(config, Config {
numbers: vec![1, 2, 3],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 7],
});
let config: Config = figment.select("release").extract()?;
assert_eq!(config, Config {
numbers: vec![6, 7, 8],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 7],
});
Ok(())
});Sourcepub fn profile<P: Into<Profile>>(self, profile: P) -> Self
pub fn profile<P: Into<Profile>>(self, profile: P) -> Self
Set the profile to emit data to when nesting is disabled.
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map, Profile};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config { nested: NestedConfig }
#[derive(Debug, PartialEq, Deserialize)]
struct NestedConfig { value: u8 }
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/nested.toml", r#"
value = 123
"#);
let provider = Toml::directory("cfg").profile("debug");
let figment = Figment::from(provider).select("debug");
let config: Config = figment.extract()?;
assert_eq!(config.nested, NestedConfig { value: 123 });
let result: Result<Config, _> = figment.select(Profile::Default).extract();
assert!(result.is_err(), "extract() should have errored but there was a value in the default profile");
Ok(())
});Sourcepub fn merge(self) -> Self
pub fn merge(self) -> Self
Set the conflict resolution strategy to
- prefer values in files that are lower down in the directory tree
- override conflicting arrays instead of appending
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config {
numbers: Vec<usize>,
untyped: Map<String, usize>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelOne {
one: Config,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelTwo {
two: NestLevelOne,
}
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/two.toml", r#"
[one.untyped]
global = 0
hi = 7
[one]
numbers = [1, 2, 3]
"#)?;
jail.create_dir("cfg/two")?;
jail.create_file("cfg/two/one.toml", r#"
numbers = [6, 7, 8]
[untyped]
hi = 8
foo = 42
"#)?;
// Set conflict resolution strategy via `merge()`.
let figment = Figment::from(Toml::directory("cfg").merge());
let config: NestLevelTwo = figment.extract()?;
assert_eq!(config.two.one, Config {
numbers: vec![6, 7, 8],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 8, "foo".into() => 42],
});
Ok(())
});Sourcepub fn join(self) -> Self
pub fn join(self) -> Self
Set the conflict resolution strategy to
- prefer values in files that are higher up in the directory tree
- override conflicting arrays instead of appending
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config {
numbers: Vec<usize>,
untyped: Map<String, usize>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelOne {
one: Config,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelTwo {
two: NestLevelOne,
}
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/two.toml", r#"
[one.untyped]
global = 0
hi = 7
[one]
numbers = [1, 2, 3]
"#)?;
jail.create_dir("cfg/two")?;
jail.create_file("cfg/two/one.toml", r#"
numbers = [6, 7, 8]
[untyped]
hi = 8
foo = 42
"#)?;
// Set conflict resolution strategy via `join()`.
let figment = Figment::from(Toml::directory("cfg").join());
let config: NestLevelTwo = figment.extract()?;
assert_eq!(config.two.one, Config {
numbers: vec![1, 2, 3],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 7, "foo".into() => 42],
});
Ok(())
});Sourcepub fn admerge(self) -> Self
pub fn admerge(self) -> Self
Set the conflict resolution strategy to
- prefer values in files that are lower down in the directory tree
- append conflicting arrays instead of overriding
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config {
numbers: Vec<usize>,
untyped: Map<String, usize>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelOne {
one: Config,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelTwo {
two: NestLevelOne,
}
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/two.toml", r#"
[one.untyped]
global = 0
hi = 7
[one]
numbers = [1, 2, 3]
"#)?;
jail.create_dir("cfg/two")?;
jail.create_file("cfg/two/one.toml", r#"
numbers = [6, 7, 8]
[untyped]
hi = 8
foo = 42
"#)?;
// Set conflict resolution strategy via `admerge()`.
let figment = Figment::from(Toml::directory("cfg").admerge());
let config: NestLevelTwo = figment.extract()?;
assert_eq!(config.two.one, Config {
numbers: vec![1, 2, 3, 6, 7, 8],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 8, "foo".into() => 42],
});
Ok(())
});Sourcepub fn adjoin(self) -> Self
pub fn adjoin(self) -> Self
Set the conflict resolution strategy to
- prefer values in files that are higher up in the directory tree
- append conflicting arrays instead of overriding
use serde::Deserialize;
use figment::{Figment, Jail, providers::{Format, Toml}, value::Map};
use figment_directory::FormatExt as _;
#[derive(Debug, PartialEq, Deserialize)]
struct Config {
numbers: Vec<usize>,
untyped: Map<String, usize>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelOne {
one: Config,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NestLevelTwo {
two: NestLevelOne,
}
Jail::expect_with(|jail| {
jail.create_dir("cfg")?;
jail.create_file("cfg/two.toml", r#"
[one.untyped]
global = 0
hi = 7
[one]
numbers = [1, 2, 3]
"#)?;
jail.create_dir("cfg/two")?;
jail.create_file("cfg/two/one.toml", r#"
numbers = [6, 7, 8]
[untyped]
hi = 8
foo = 42
"#)?;
// Set conflict resolution strategy via `adjoin()`.
let figment = Figment::from(Toml::directory("cfg").adjoin());
let config: NestLevelTwo = figment.extract()?;
assert_eq!(config.two.one, Config {
numbers: vec![1, 2, 3, 6, 7, 8],
untyped: figment::util::map!["global".into() => 0, "hi".into() => 7, "foo".into() => 42],
});
Ok(())
});