Default values

In Schematic, there are 2 forms of default values:

  • The first is applied through the partial configuration, is defined with the #[setting] attribute, and is the first layer to be merged.
  • The second is on the final configuration itself, and uses the Default trait to generate the final value if none was provided. This acts more like a fallback.

To define a default value, use the #[setting(default)] attribute. The default attribute field is used for declaring primitive values, like numbers, strings, and booleans, but can also be used for array and tuple literals, as well as function (mainly for from()) and macros calls.

#![allow(unused)]
fn main() {
#[derive(Config)]
struct AppConfig {
	#[setting(default = "/")]
	pub base: String,

	#[setting(default = 3000)]
	pub port: usize,

	#[setting(default = true)]
	pub secure: bool,

	#[setting(default = vec!["localhost".into()])]
	pub allowed_hosts: Vec<String>,
}
}

For enums, the default field takes no value, and simply marks which variant to use as the default.

#![allow(unused)]
fn main() {
#[derive(Config)]
enum Host {
	#[setting(default)]
	Local,
	Remote(HostConfig),
}
}

Handler function

If you need more control or need to calculate a complex value, you can pass a reference to a function to call. This function receives the context as the first argument, and can return an optional value. If None is returned, the Default value will be used instead.

#![allow(unused)]
fn main() {
fn find_unused_port(ctx: &Context) -> DefaultValueResult<usize> {
	let port = do_find()?;

	Ok(Some(port))
}

#[derive(Config)]
struct AppConfig {
	#[setting(default = find_unused_port)]
	pub port: usize,
}
}

Context handling

If you’re not using context, you can use () as the context type, or rely on generic inferrence.

#![allow(unused)]
fn main() {
fn using_unit_type(_: &()) -> DefaultValueResult<usize> {
	// ...
}

fn using_generics<C>(_: &C) -> DefaultValueResult<usize> {
	// ...
}
}