use std::path::Path;
use clap::Subcommand;
use spec_spine_core::effective_bypass_prefixes;
use spec_spine_types::{EffectiveConfig, Error};
use crate::load_repo_config;
use crate::out;
#[derive(Subcommand, Debug)]
pub enum ConfigAction {
Show {
#[arg(long)]
json: bool,
},
}
pub fn run(repo: &Path, action: &ConfigAction) -> Result<u8, Error> {
let ConfigAction::Show { json } = action;
let cfg = load_repo_config(repo)?;
let effective = EffectiveConfig::new(&cfg, effective_bypass_prefixes(&cfg));
if *json {
let s = spec_spine_core::read_document(
&effective,
spec_spine_core::Versioning::Preexisting("config_version"),
)?;
out::line(format_args!("{}", s.trim_end_matches('\n')));
return Ok(0);
}
out::line(format_args!(
"config_version = \"{}\"",
effective.config_version
));
render(&effective);
Ok(0)
}
fn render(e: &EffectiveConfig) {
out::line(format_args!("\n[manifest]"));
kv("metadata_namespace", &e.manifest.metadata_namespace);
out::line(format_args!("\n[domains]"));
list("allowed", &e.domains.allowed);
out::line(format_args!("\n[kind]"));
list("allowed", &e.kind.allowed);
out::line(format_args!("\n[layout]"));
kv("specs_dir", &e.layout.specs_dir);
kv("derived_dir", &e.layout.derived_dir);
kv("standards_dir", &e.layout.standards_dir);
kv("schemas_dir", &e.layout.schemas_dir);
kv("state_dir", &e.layout.state_dir);
kv("cargo_workspace", &e.layout.cargo_workspace);
list("npm_workspaces", &e.layout.npm_workspaces);
list(
"standalone_rust_workspaces",
&e.layout.standalone_rust_workspaces,
);
list("standalone_npm_packages", &e.layout.standalone_npm_packages);
out::line(format_args!("\n[index]"));
list("extra_hashed_inputs", &e.index.extra_hashed_inputs);
list("resolver_exclusions", &e.index.resolver_exclusions);
out::line(format_args!("\n[branding]"));
kv("compiler_id", &e.branding.compiler_id);
kv("indexer_id", &e.branding.indexer_id);
out::line(format_args!("\n[coupling]"));
kv("waiver_keyword", &e.coupling.waiver_keyword);
flag("require_ownership", e.coupling.require_ownership);
flag(
"auto_waive_dependency_only",
e.coupling.auto_waive_dependency_only,
);
out::line(format_args!(" bypass_prefixes:"));
let width = e
.coupling
.bypass_prefixes
.iter()
.map(|b| b.prefix.chars().count())
.max()
.unwrap_or(0);
for b in &e.coupling.bypass_prefixes {
let sources: Vec<&str> = b.sources.iter().map(|s| s.label()).collect();
out::line(format_args!(
" {:<width$} ({})",
b.prefix,
sources.join(", "),
width = width
));
}
out::line(format_args!("\n[coverage]"));
list("governed_scope", &e.coverage.governed_scope);
list(
"governed_scope_exclusions",
&e.coverage.governed_scope_exclusions,
);
out::line(format_args!("\n[provenance.uri_schemes]"));
for (k, v) in &e.provenance.uri_schemes {
out::line(format_args!(" {k} = \"{v}\""));
}
out::line(format_args!("\n[frontmatter]"));
list("extra_known_keys", &e.frontmatter.extra_known_keys);
out::line(format_args!("\n[meta]"));
match &e.meta.required_version {
Some(v) => kv("required_version", v),
None => out::line(format_args!(" required_version = (unpinned)")),
}
out::line(format_args!("\n[lint]"));
flag(
"require_ordinal_monotonic_depends_on",
e.lint.require_ordinal_monotonic_depends_on,
);
}
fn kv(key: &str, value: &str) {
out::line(format_args!(" {key} = \"{value}\""));
}
fn flag(key: &str, value: bool) {
out::line(format_args!(" {key} = {value}"));
}
fn list(key: &str, values: &[String]) {
if values.is_empty() {
out::line(format_args!(" {key} = []"));
return;
}
let quoted: Vec<String> = values.iter().map(|v| format!("\"{v}\"")).collect();
out::line(format_args!(" {key} = [{}]", quoted.join(", ")));
}