Struct spirit::Builder

source ·
pub struct Builder<O = Empty, C = Empty> { /* private fields */ }
Expand description

The builder of Spirit.

This is returned by the Spirit::new.

Implementations§

Apply a config helper to the builder.

For more information see CfgHelper.

Check if this is the first call with the given type.

Some helpers share common part. This common part makes sense to register just once, so this can be used to check that. The first call with given type returns true, any future ones with the same type return false.

The method has no direct effect on the future spirit constructed from the builder and works only as a note for future helpers that want to manipulate the builder.

A higher-level interface is the with_singleton method.

Examples
use spirit::{Empty, Spirit};

let mut builder = Spirit::<Empty, Empty>::new();

struct X;
struct Y;

assert!(builder.singleton::<X>());
assert!(!builder.singleton::<X>());
assert!(builder.singleton::<Y>());

Apply a ’Helper` to the builder.

Apply the first Helper of the type.

This applies the passed helper, but only if a helper with the same hasn’t yet been applied (or the singleton called manually).

Note that different instances of the same type of a helper can act differently, but are still considered the same type. This means the first instance wins. This is considered a feature ‒ many other helpers need some environment to run in (like tokio). The helpers try to apply a default configuration, but the user can apply a specific configuration first.

Add a closure run before the main body.

The run will first execute all closures submitted through this method before running the real body. They are run in the order of submissions.

The purpose of this is mostly integration with helpers ‒ they often need some last minute preparation.

In case of using only build, the bodies are composed into one object and returned as part of the result (the inner body).

Errors

If any of the before-bodies in the chain return an error, the processing ends there and the error is returned right away.

Examples
Spirit::<Empty, Empty>::new()
    .before_body(|_spirit| {
        println!("Run first");
        Ok(())
    }).run(|_spirit| {
        println!("Run second");
        Ok(())
    });

A callback that is run after the building started and the command line is parsed, but even before the first configuration is loaded.

If the callback returns an error, the building is aborted.

Wrap the body run by the run into this closure.

The inner body is passed as an object with a run method, not as a closure, due to a limitation around boxed FnOnce.

It is expected the wrapper executes the inner body as part of itself and propagates any returned error.

In case of multiple wrappers, the ones submitted later on are placed inside the sooner ones ‒ the first one is the outermost.

In case of using only build, all the wrappers composed together are returned as part of the result.

Examples
Spirit::<Empty, Empty>::new()
    .body_wrapper(|_spirit, inner| {
        println!("Run first");
        inner.run()?;
        println!("Run third");
        Ok(())
    }).run(|_spirit| {
        println!("Run second");
        Ok(())
    });

Sets the configuration paths in case the user doesn’t provide any.

This replaces any previously set default paths. If none are specified and the user doesn’t specify any either, no config is loaded (but it is not an error, simply the defaults will be used, if available).

Specifies the default configuration.

This „loads“ the lowest layer of the configuration from the passed string. The expected format is TOML.

Enables loading configuration from environment variables.

If this is used, after loading the normal configuration files, the environment of the process is examined. Variables with the provided prefix are merged into the configuration.

Examples
extern crate failure;
#[macro_use]
extern crate serde_derive;
extern crate spirit;

use failure::Error;
use spirit::{Empty, Spirit};

#[derive(Default, Deserialize)]
struct Cfg {
    message: String,
}

const DEFAULT_CFG: &str = r#"
message = "Hello"
"#;

fn main() {
    Spirit::<Empty, Cfg>::new()
        .config_defaults(DEFAULT_CFG)
        .config_env("HELLO")
        .run(|spirit| -> Result<(), Error> {
            println!("{}", spirit.config().message);
            Ok(())
        });
}

If run like this, it’ll print Hi. The environment takes precedence ‒ even if there was configuration file and it set the message, the Hi here would win.

HELLO_MESSAGE="Hi" ./hello

Configures a config dir filter for a single extension.

Sets the config directory filter (see config_filter) to one matching this single extension.

Configures a config dir filter for multiple extensions.

Sets the config directory filter (see config_filter) to one matching files with any of the provided extensions.

Sets a configuration dir filter.

If the user passes a directory path instead of a file path, the directory is traversed (every time the configuration is reloaded, so if files are added or removed, it is reflected) and files passing this filter are merged into the configuration, in the lexicographical order of their file names.

There’s ever only one filter and the default one passes no files (therefore, directories are ignored by default).

The filter has no effect on files, only on loading directories. Only files directly in the directory are loaded ‒ subdirectories are not traversed.

For more convenient ways to set the filter, see config_ext and config_exts.

Adds another config validator to the chain.

The validators are there to check, possibly modify and possibly refuse a newly loaded configuration.

The callback is passed three parameters:

  • The old configuration.
  • The new configuration (possible to modify).
  • The command line options.

They are run in order of being set. Each one can pass arbitrary number of results, where a result can carry a message (of different severities) that ends up in logs and actions to be taken if the validation succeeds or fails.

If none of the validator returns an error-level message, the validation passes. After it is determined if the configuration passed, either all the success or failure actions are run.

The actions

Sometimes, the only way to validate a piece of config is to try it out ‒ like when you want to open a listening socket, you don’t know if the port is free. But you can’t activate and apply just yet, because something further down the configuration might still fail.

So, you open the socket (or create an error result) and store it into the success action to apply it later on. If something fails, the action is dropped and the socket closed.

The failure action lets you roll back (if it isn’t done by simply dropping the thing).

If the validation and application steps can be separated (you can check if something is OK by just „looking“ at it ‒ like with a regular expression and using it can’t fail), you don’t have to use them, just use verification and on_config separately.

TODO: Threads, deadlocks

Examples

TODO

Adds a callback for notification about new configurations.

The callback is called once a new configuration is loaded and successfully validated.

TODO: Threads, deadlocks

Adds a callback for reacting to a signal.

The Spirit reacts to some signals itself, in its own service thread. However, it is also possible to hook into any signals directly (well, any except the ones that are off limits).

These are not run inside the real signal handler, but are delayed and run in the service thread. Therefore, restrictions about async-signal-safety don’t apply to the hook.

TODO: Threads, deadlocks

Adds a callback executed once the Spirit decides to terminate.

This is called either when someone calls terminate or when a termination signal is received.

Note that there are ways the application may terminate without calling these hooks ‒ for example terminating the main thread, or aborting.

TODO: Threads, deadlocks

Finish building the Spirit.

This transitions from the configuration phase of Spirit to actually creating it. This loads the configuration and executes it for the first time. It launches the background thread for listening to signals and reloading configuration if the background_thread parameter is set to true.

This starts listening for signals, loads the configuration for the first time and starts the background thread.

This version returns the spirit (or error) and error handling is up to the caller. If you want spirit to take care of nice error logging (even for your application’s top level errors), use run.

Result

On success, this returns three things:

  • The spirit handle, allowing to manipulate it (shutdown, read configuration, …)
  • The before-body hooks (see before_body.
  • The body wrappers (body_wrapper).

The two latter ones are often set by helpers, so you should not ignore them.

Warning

If asked to go to background (when you’re using the spirit-daemonize crate, this uses fork. Therefore, start any threads after you call build (or from within run), or you’ll lose them ‒ only the thread doing fork is preserved across it.

Build the spirit and run the application, handling all relevant errors.

In case an error happens (either when creating the Spirit, or returned by the callback), the errors are logged (either to the place where logs are sent to in configuration, or to stderr if the error happens before logging is initialized ‒ for example if configuration can’t be read). The application then terminates with failure exit code.

It first wraps all the calls in the provided wrappers (body_wrapper) and runs the before body hooks (before_body) before starting the real body provided as parameter. These are usually provided by helpers.

use std::thread;
use std::time::Duration;

use spirit::{Empty, Spirit};

Spirit::<Empty, Empty>::new()
    .run(|spirit| {
        while !spirit.is_terminated() {
            // Some reasonable work here
            thread::sleep(Duration::from_millis(100));
        }

        Ok(())
    });

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.