foundry-conventions 0.1.0

Config layer precedence and decision tracing for Rust Foundry
Documentation
use foundry_schema::{merge, FoundryConfig, PartialConfig};
use foundry_types::DecisionTrace;

#[derive(Debug, Clone, Default)]
pub struct ConfigLayers {
    pub profile: PartialConfig,
    pub project: PartialConfig,
    pub companion: PartialConfig,
    pub explicit: PartialConfig,
}

pub fn resolve_config(layers: ConfigLayers) -> (FoundryConfig, Vec<DecisionTrace>) {
    let mut cfg = FoundryConfig::default();
    let mut traces = vec![DecisionTrace {
        field: "all".into(),
        source: "built_in_defaults".into(),
        value: format!("{}:{}", cfg.archetype, cfg.profile),
    }];

    cfg = merge(&cfg, &layers.profile);
    append_trace(&mut traces, "profile_defaults", &cfg);

    cfg = merge(&cfg, &layers.project);
    append_trace(&mut traces, "project_foundry_toml", &cfg);

    cfg = merge(&cfg, &layers.companion);
    append_trace(&mut traces, "companion_toml", &cfg);

    cfg = merge(&cfg, &layers.explicit);
    append_trace(&mut traces, "explicit_input", &cfg);

    (cfg, traces)
}

fn append_trace(traces: &mut Vec<DecisionTrace>, source: &str, cfg: &FoundryConfig) {
    traces.push(DecisionTrace {
        field: "project_name".into(),
        source: source.into(),
        value: cfg.project_name.clone(),
    });
    traces.push(DecisionTrace {
        field: "archetype".into(),
        source: source.into(),
        value: cfg.archetype.to_string(),
    });
    traces.push(DecisionTrace {
        field: "template_engine".into(),
        source: source.into(),
        value: format!("{:?}", cfg.template_engine),
    });
    traces.push(DecisionTrace {
        field: "profile".into(),
        source: source.into(),
        value: cfg.profile.clone(),
    });
    traces.push(DecisionTrace {
        field: "run_post_gen_checks".into(),
        source: source.into(),
        value: cfg.run_post_gen_checks.to_string(),
    });
}