schematic 0.20.0

A layered serde configuration and schema library.
Documentation
mod utils;

use schematic::*;
use utils::*;

#[derive(Debug, Config)]
pub struct Config {
    boolean: bool,
    string: String,
    number: usize,
    vector: Vec<String>,
}

fn get_url(path: &str) -> String {
    format!(
        "https://raw.githubusercontent.com/moonrepo/schematic/master/crates/schematic/tests/__fixtures__/{path}"
    )
}

#[test]
fn can_create_url_source() {
    let source = Source::new("https://some/path/config.yml", None).unwrap();

    assert_eq!(
        source,
        Source::Url {
            url: "https://some/path/config.yml".to_owned(),
        }
    );
}

#[test]
#[should_panic(expected = "HttpsOnly")]
fn errors_on_http() {
    ConfigLoader::<Config>::new()
        .url("http://some/path/config.yml")
        .unwrap()
        .load()
        .unwrap();
}

#[test]
#[should_panic(expected = "HttpsOnly")]
fn errors_on_www() {
    ConfigLoader::<Config>::new()
        .url("www.domain.com/some/path/config.yml")
        .unwrap()
        .load()
        .unwrap();
}

// A non-2xx response is an error, rather than an error page handed to the
// parser as if it were config
#[test]
fn errors_on_a_missing_url() {
    let error = ConfigLoader::<Config>::new()
        .url(get_url("yaml/does-not-exist.yml"))
        .unwrap()
        .load()
        .err()
        .unwrap();

    assert!(
        matches!(error, ConfigError::ReadUrlFailed { .. }),
        "expected a read failure, got {error:?}"
    );
}

// `ureq` is blocking but never builds a runtime of its own, so loading from
// inside one blocks the thread instead of panicking the way a client with a
// private runtime would
#[tokio::test]
async fn loads_from_within_an_async_runtime() {
    let result = ConfigLoader::<Config>::new()
        .url(get_url("yaml/one.yml"))
        .unwrap()
        .load()
        .unwrap();

    assert_eq!(result.config.string, "foo");
}

#[cfg(feature = "json")]
#[test]
fn loads_json_files() {
    let result = ConfigLoader::<Config>::new()
        .url(get_url("json/one.json"))
        .unwrap()
        .url(get_url("json/two.json"))
        .unwrap()
        .url(get_url("json/three.json"))
        .unwrap()
        .url(get_url("json/four.json"))
        .unwrap()
        .url(get_url("json/five.json"))
        .unwrap()
        .load()
        .unwrap();

    assert!(!result.config.boolean);
    assert_eq!(result.config.string, "bar");
    assert_eq!(result.config.number, 123);
    assert_eq!(result.config.vector, vec!["x", "y", "z"]);
}

#[cfg(feature = "pkl")]
#[test]
fn loads_pkl_files() {
    use starbase_sandbox::create_empty_sandbox;

    let sandbox = create_empty_sandbox();

    let result = ConfigLoader::<Config>::new()
        .set_cacher(SandboxCacher {
            root: sandbox.path().to_owned(),
        })
        .url(get_url("pkl/one.pkl"))
        .unwrap()
        .url(get_url("pkl/two.pkl"))
        .unwrap()
        .url(get_url("pkl/three.pkl"))
        .unwrap()
        .url(get_url("pkl/four.pkl"))
        .unwrap()
        .url(get_url("pkl/five.pkl"))
        .unwrap()
        .load()
        .unwrap();

    assert!(!result.config.boolean);
    assert_eq!(result.config.string, "bar");
    assert_eq!(result.config.number, 123);
    assert_eq!(result.config.vector, vec!["x", "y", "z"]);
}

#[cfg(feature = "toml")]
#[test]
fn loads_toml_files() {
    let result = ConfigLoader::<Config>::new()
        .url(get_url("toml/one.toml"))
        .unwrap()
        .url(get_url("toml/two.toml"))
        .unwrap()
        .url(get_url("toml/three.toml"))
        .unwrap()
        .url(get_url("toml/four.toml"))
        .unwrap()
        .url(get_url("toml/five.toml"))
        .unwrap()
        .load()
        .unwrap();

    assert!(!result.config.boolean);
    assert_eq!(result.config.string, "bar");
    assert_eq!(result.config.number, 123);
    assert_eq!(result.config.vector, vec!["x", "y", "z"]);
}

#[test]
fn loads_yaml_files() {
    let result = ConfigLoader::<Config>::new()
        .url(get_url("yaml/one.yml"))
        .unwrap()
        .url(get_url("yaml/two.yml"))
        .unwrap()
        .url(get_url("yaml/three.yml"))
        .unwrap()
        .url(get_url("yaml/four.yml"))
        .unwrap()
        .url(get_url("yaml/five.yml"))
        .unwrap()
        .load()
        .unwrap();

    assert!(!result.config.boolean);
    assert_eq!(result.config.string, "bar");
    assert_eq!(result.config.number, 123);
    assert_eq!(result.config.vector, vec!["x", "y", "z"]);
}