Skip to main content

Load

Trait Load 

Source
pub trait Load {
    // Required methods
    fn name(&self) -> &str;
    fn supported_source_list(&self) -> Vec<String>;
    fn load(&self, source: Source) -> Result<Vec<Payload>, Error>;
}
Expand description

Loads raw configuration bytes from a declared source.

Implement this to add a new source kind (protocol, service, database, …). This is the first stage of the pipeline: it only fetches bytes, it does not parse them — Payload::content is handed to the parser stage unchanged.

§Contract

  • load takes ownership of one Source and returns one Payload per configuration entry found. A single source may expand to many entries (e.g. every file in a directory) → return many payloads; finding nothing is Ok(vec![]), not an error.
  • Set Payload::source on each entry to the concrete resource loaded, not the original source — clone it and narrow with Source::with_resource. This keeps diagnostics precise.
  • Use Payload::maybe_name for the entry name (None merges into the root with all other unnamed entries) and Payload::maybe_format as a parser hint (e.g. json).
  • Follow the lowercase convention: when your lowercase option is true (recommended default), lower-case names and formats so entries merge predictably across sources.

§Reading options

Options declared on the source (e.g. file(ignore=[not-found])) are available via Source::options. Look each up with Options::get, convert with the typed accessors (OptionValue::as_bool, OptionValue::as_string, OptionValue::as_list, …), and on a type mismatch build the reason from OptionValue::type_name. It is good practice to reject unknown keys by iterating Options::keys and returning Error::InvalidOption — see the file loader’s load for a complete worked pattern.

§Choosing an error

§Registering

Pass an instance to tanzim::Config::with_loader. The pipeline dispatches each source to the first loader whose supported_source_list contains the source string, so it may advertise several (e.g. ["http", "https"]). For a one-off loader you don’t want to define a type for, use closure::Closure instead of implementing this trait.

§Example — collecting specific environment variables

A loader that reads the variable names listed in its keys option and returns them as one env-format payload. It shows the whole contract: reading a typed option, mapping failures to the right Error variant, and building a Payload.

use std::env;
use tanzim_load::{Error, Load, Payload, Source};

struct SelectedEnv;

impl Load for SelectedEnv {
    fn name(&self) -> &str { "selected-env" }
    fn supported_source_list(&self) -> Vec<String> { vec!["selected-env".into()] }

    fn load(&self, source: Source) -> Result<Vec<Payload>, Error> {
        // Read the `keys` option — a required list of variable names.
        let value = source.options().get("keys").ok_or_else(|| Error::InvalidOption {
            loader: self.name().into(),
            key: "keys".into(),
            reason: "required".into(),
        })?;
        let keys = value.as_list().ok_or_else(|| Error::InvalidOption {
            loader: self.name().into(),
            key: "keys".into(),
            reason: format!("expected list, found {}", value.type_name()),
        })?;

        // Collect each requested variable into a `KEY="value"` line.
        let mut lines = Vec::new();
        for item in keys {
            let key = item.as_string().ok_or_else(|| Error::InvalidOption {
                loader: self.name().into(),
                key: "keys".into(),
                reason: format!("expected string, found {}", item.type_name()),
            })?;
            let val = env::var(key).map_err(|_| Error::NotFound {
                loader: self.name().into(),
                resource: source.resource().into(),
                item: format!("environment variable `{key}`"),
            })?;
            lines.push(format!("{key}={val:?}"));
        }

        Ok(vec![Payload {
            source,
            maybe_name: None,                 // unnamed → merges into the config root
            maybe_format: Some("env".into()), // parsed by the `env` parser
            content: lines.join("\n").into_bytes(),
        }])
    }
}

// SAFETY: example-only; single-threaded doctest env vars.
unsafe {
    env::set_var("DB_HOST", "localhost");
    env::set_var("DB_PORT", "5432");
}

let source = Source::parse("selected-env(keys=[DB_HOST,DB_PORT])").unwrap();

let payloads = SelectedEnv.load(source).unwrap();
let content = String::from_utf8_lossy(&payloads[0].content);
assert!(content.contains(r#"DB_HOST="localhost""#));
assert!(content.contains(r#"DB_PORT="5432""#));

Required Methods§

Source

fn name(&self) -> &str

Human-readable name used in error messages.

Source

fn supported_source_list(&self) -> Vec<String>

Source strings this loader handles (e.g. ["env"], ["file"], ["http", "https"]).

Source

fn load(&self, source: Source) -> Result<Vec<Payload>, Error>

Load raw bytes from the source. Returns one Payload per config entry found.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§