Skip to main content

opys_engine/commands/
config.rs

1//! `opys config …` — project-config commands.
2//!
3//! Currently just `config init`, which scaffolds the opinionated default
4//! `opys.toml` for the upcoming universal typed-document engine. Nothing reads
5//! that file yet; this only generates it so the config shape can be reviewed and
6//! iterated on. Future subcommands (`config validate`, `config show`) belong
7//! here too.
8
9use crate::error::{usage, Result};
10use crate::project::{find_root, start_dir};
11use crate::project_config::ProjectConfig;
12use crate::templates::DEFAULT_OPYS_CONFIG;
13use crate::Ctx;
14
15/// Write the default `opys.toml` at the project root, without overwriting.
16pub fn init(ctx: &Ctx) -> Result<()> {
17    let root = start_dir(&ctx.root)?;
18    std::fs::create_dir_all(&root)?;
19    let path = root.join("opys.toml");
20    if path.exists() {
21        println!("{} already exists; leaving it untouched", path.display());
22    } else {
23        std::fs::write(&path, DEFAULT_OPYS_CONFIG)?;
24        println!(
25            "created {} — edit it to model your document types",
26            path.display()
27        );
28    }
29    Ok(())
30}
31
32/// Parse `opys.toml` (found by searching upward) and report any well-formedness
33/// problems. Returns `1` when the config has problems (mirroring `verify`), `0`
34/// when clean; a missing file or TOML syntax error surfaces as a hard error.
35pub fn validate(ctx: &Ctx) -> Result<i32> {
36    let start = start_dir(&ctx.root)?;
37    let root = find_root(&start).ok_or_else(|| {
38        usage(format!(
39            "no opys.toml found in {} or any parent directory — run `opys config init`",
40            start.display()
41        ))
42    })?;
43    let cfg = ProjectConfig::load(&root.join("opys.toml"))?;
44    let problems = cfg.validate();
45    if problems.is_empty() {
46        println!(
47            "config: OK ({} types, {} rules)",
48            cfg.types.len(),
49            cfg.rules.len()
50        );
51        Ok(0)
52    } else {
53        eprintln!("config: {} problem(s)", problems.len());
54        for p in &problems {
55            eprintln!("  {p}");
56        }
57        Ok(1)
58    }
59}