use std::path::PathBuf; use anyhow::{bail, Result}; use clap::{Parser, Subcommand}; use crate::compare::compare; use crate::manifest::ArtifactManifest; use crate::{BuildPlan, ImageSpec}; #[derive(Debug, Parser)] #[command(name = "alt-controller-image", about = "Native ALT image builder")] pub struct Cli { #[command(subcommand)] pub command: Command, } #[derive(Debug, Subcommand)] pub enum Command { /// Validate an image specification and render its build intent. Plan { #[arg(long)] spec: PathBuf, }, /// Build an image from a specification. Build { #[arg(long)] spec: PathBuf, #[arg(long)] workspace: PathBuf, #[arg(long)] output: PathBuf, }, /// Inspect a built artifact. Inspect { #[arg(long)] artifact: PathBuf, }, /// Compare two built artifacts semantically. Compare { #[arg(long)] left: PathBuf, #[arg(long)] right: PathBuf, }, } pub fn run(cli: Cli) -> Result<()> { match cli.command { Command::Plan { spec } => { let plan = BuildPlan::compile(ImageSpec::load(&spec)?)?; println!("target: {}", plan.spec().target.name); println!("architecture: {}", plan.spec().target.architecture.as_str()); println!("format: tar"); println!("stages:"); for (index, stage) in plan.stages().iter().enumerate() { println!(" {}. {stage}", index + 1); } Ok(()) } Command::Build { .. } => bail!("build execution is not available yet"), Command::Inspect { artifact } => { let manifest = ArtifactManifest::load(&artifact)?; println!("{}", toml::to_string_pretty(&manifest)?); Ok(()) } Command::Compare { left, right } => { let left = ArtifactManifest::load(&left)?; let right = ArtifactManifest::load(&right)?; let report = compare(&left, &right); print!("{}", report.render()); if report.is_equivalent() { Ok(()) } else { bail!("artifacts differ semantically") } } } }