use clap::{Parser, Subcommand};
use miette::Result;
use tracing_subscriber::EnvFilter;
mod baseline;
mod commands;
mod config;
mod graph;
mod parser;
mod report;
mod rules;
mod scanner;
#[derive(Parser)]
#[command(
name = "ark",
version,
about = "Architectural boundary enforcer for .NET solutions",
long_about = None
)]
struct Cli {
#[arg(short, long, default_value = ".")]
root: String,
#[arg(short, long, default_value = "architecture.toml")]
config: String,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Check {
#[arg(long)]
strict: bool,
#[arg(long)]
no_baseline: bool,
},
Baseline,
Graph {
#[arg(short, long, default_value = "mermaid")]
format: String,
#[arg(short, long)]
output: Option<String>,
},
Init,
Explain {
project: String,
},
}
fn resolve_config(root: &str, config: &str) -> String {
let p = std::path::Path::new(config);
if p.is_relative() {
std::path::Path::new(root)
.join(p)
.to_string_lossy()
.into_owned()
} else {
config.to_owned()
}
}
fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
let cli = Cli::parse();
let config = resolve_config(&cli.root, &cli.config);
match cli.command {
Commands::Check {
strict,
no_baseline,
} => commands::check::run(&cli.root, &config, strict, no_baseline),
Commands::Baseline => commands::baseline::run(&cli.root, &config),
Commands::Graph { format, output } => {
commands::graph::run(&cli.root, &config, &format, output.as_deref())
}
Commands::Init => commands::init::run(&cli.root),
Commands::Explain { project } => commands::explain::run(&cli.root, &config, &project),
}
}