ark-cli 0.1.1

Architectural boundary enforcer for .NET solutions
use miette::{IntoDiagnostic, Result, miette};
use std::path::Path;

const TEMPLATE: &str = r#"# Ark Architecture Configuration
# Generated by `ark init` — edit to match your solution.

[[layers]]
name = "Presentation"
patterns = ["*.Api", "*.Web", "*.Host"]

[[layers]]
name = "Application"
patterns = ["*.Application", "*.UseCases"]

[[layers]]
name = "Domain"
patterns = ["*.Domain", "*.Core"]

[[layers]]
name = "Infrastructure"
patterns = ["*.Infrastructure", "*.Persistence", "*.Adapters"]

# Presentation may call Application
[[dependency_rules]]
from = "Presentation"
to = "Application"
allowed = true

# Application may call Domain
[[dependency_rules]]
from = "Application"
to = "Domain"
allowed = true

# Infrastructure implements Domain interfaces
[[dependency_rules]]
from = "Infrastructure"
to = "Domain"
allowed = true

# Domain must NOT depend on anything else
[[dependency_rules]]
from = "Domain"
to = "Application"
allowed = false

[[dependency_rules]]
from = "Domain"
to = "Presentation"
allowed = false

[[dependency_rules]]
from = "Domain"
to = "Infrastructure"
allowed = false

[[package_policies]]
layer = "Domain"
# Domain should stay pure — no EF Core, no HTTP clients
forbidden = ["Microsoft.EntityFrameworkCore", "System.Net.Http"]

ignore_patterns = ["*.Tests", "*.Specs", "*.IntegrationTests"]
"#;

pub fn run(root: &str) -> Result<()> {
    let dest = Path::new(root).join("architecture.toml");

    if dest.exists() {
        return Err(miette!(
            "architecture.toml already exists at {:?}. \
            Delete it first or edit it directly.",
            dest
        ));
    }

    std::fs::write(&dest, TEMPLATE).into_diagnostic()?;
    println!("Created {:?}", dest);
    println!("Edit it to match your solution, then run `ark check`.");
    Ok(())
}