Module spirit::guide::configuration[][src]

Expand description

Loading and handling configuration

Apart from the fact that one doesn’t have to use the whole Spirit to load configuration, you can use the Loader if you don’t need the rest of the stuff, there’s nothing interesting about it. Or, is there? Well, there’s a lot of little details you might want to use.

Sources of configuration

The configuration comes from different sources and is merged together. The last one to set a value wins.

  • A configuration embedded inside the program through config_defaults.
  • Configuration loaded from files. They are loaded in the order they appear on the command line. If it specifies a directory, all config files in there are loaded, in the order of sorting (the list is made anew at each reload). If no path is set on the command line, the value set through config_default_paths.
  • Things loaded from environment variables, if a prefix is configured.
  • Last, the values set on command line through --config-override, are used.

Configuration phases

Every time the configuration is loaded, it goes through few phases. If any of them fails, the whole loading fails. If it’s the first time, the application aborts. On any future reload, if it fails, the error is logged, but the application continues with previous configuration.

  • First, the configuration is composed together and merged.
  • It is deserialized with serde and the configuration is filled into the provided structure.
  • The validators are run. They can schedule an action to run on commit, when everything succeeds, or on abort, if it fails. It allows to either postpone actually activating the configuration, or rolling back an attempt. Only once all of them succeed, the new configuration is activated.
  • Then the on_config hooks are run.

Using the configuration

One can use the configuration in multiple ways:

  • From the validator, try it out and schedule using it or throwing it out. This is useful if the configuration may still be invalid even if it is the right type.
  • With the on_config hook, if it can’t possibly fail.
  • Look into it each time it is needed. There is the config method.

A variation of the last one is propagating it through parts of program through arc-swap. It also have the access module, to „slice“ the shared configuration and provide only part of it to different parts of program.

#[derive(Clone, Debug, Default, Deserialize)]
struct Cfg {
    // Something goes in here
}

let cfg_store = Arc::new(ArcSwap::from_pointee(Cfg::default()));

Spirit::<Empty, Cfg>::new()
    .on_config({
        let cfg_store = Arc::clone(&cfg_store);
        move |_, cfg| cfg_store.store(cfg.clone())
    })
    .run(|_| {
        some_part_of_app(cfg_store);
        Ok(())
    });