clingwrap 0.7.0

types and functions to implement command line programs
Documentation
//! A simple example of using `clingwrap::config`.

use clingwrap::config::*;

use serde::{Deserialize, Serialize};
use tempfile::tempdir;

// This is the validated configuration: the result of loading and merging
// a set of configuration files. It doesn't match an individual
// configuration file.
#[allow(dead_code)]
#[derive(Debug)]
struct Simple {
    greeting: String,
    whom: String,
}

// This matches an individual configuration file.
#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
struct SimpleFile {
    greeting: Option<String>,
    whom: Option<String>,
}

// Implement merging two configuration files into one.
impl<'a> ConfigFile<'a> for SimpleFile {
    type Error = SimpleError;

    fn merge(&mut self, config_file: SimpleFile) -> Result<(), Self::Error> {
        if let Some(value) = &config_file.greeting {
            self.greeting = Some(value.to_string());
        }
        if let Some(value) = &config_file.whom {
            self.whom = Some(value.to_string());
        }
        Ok(())
    }
}

// Implement validating the result of merging configuration files.
#[derive(Default)]
struct SimpleValidator {}

impl ConfigValidator for SimpleValidator {
    type File = SimpleFile;
    type Valid = Simple;
    type Error = SimpleError;

    fn validate(&self, runtime: &Self::File) -> Result<Self::Valid, Self::Error> {
        Ok(Simple {
            greeting: runtime.greeting.clone().ok_or(SimpleError::Missing)?,
            whom: runtime.whom.clone().ok_or(SimpleError::Missing)?,
        })
    }
}

// Errors from loading or validating configuration files.
#[derive(Debug, thiserror::Error)]
enum SimpleError {
    #[error("required field has not been set")]
    Missing,
}

fn main() {
    env_logger::init();

    const CONFIG_1: &str = "{}";
    const CONFIG_2: &str = r#"greeting: hello
whom: world"#;

    let mut loader = ConfigLoader::default();
    let tmp = tempdir().unwrap();

    let filename = tmp.path().join("config1.json");
    std::fs::write(&filename, CONFIG_1.as_bytes()).unwrap();
    loader.require_json(&filename);

    let filename = tmp.path().join("config2.yaml");
    std::fs::write(&filename, CONFIG_2.as_bytes()).unwrap();
    loader.require_yaml(&filename);

    let validator = SimpleValidator::default();
    let valid = loader.load(None, None, &validator).unwrap();

    println!("loaded and validated configuration: {valid:#?}");
}