greentic_component/
cli.rs

1use anyhow::{Error, Result, bail};
2use clap::{Parser, Subcommand};
3
4use crate::cmd::{
5    self, build::BuildArgs, doctor::DoctorArgs, flow::FlowCommand, hash::HashArgs,
6    inspect::InspectArgs, new::NewArgs, store::StoreCommand, templates::TemplatesArgs,
7    test::TestArgs,
8};
9use crate::scaffold::engine::ScaffoldEngine;
10
11#[derive(Parser, Debug)]
12#[command(
13    name = "greentic-component",
14    about = "Toolkit for Greentic component developers",
15    version,
16    propagate_version = true,
17    arg_required_else_help = true,
18    disable_version_flag = true
19)]
20pub struct Cli {
21    #[command(subcommand)]
22    command: Commands,
23}
24
25#[derive(Subcommand, Debug)]
26enum Commands {
27    /// Scaffold a new Greentic component project
28    New(NewArgs),
29    /// List available component templates
30    Templates(TemplatesArgs),
31    /// Run component doctor checks
32    Doctor(DoctorArgs),
33    /// Inspect manifests and describe payloads
34    Inspect(InspectArgs),
35    /// Recompute manifest hashes
36    Hash(HashArgs),
37    /// Build component wasm + update config flows
38    Build(BuildArgs),
39    /// Invoke a component locally with an in-memory state/secrets harness
40    #[command(
41        long_about = "Invoke a component locally with in-memory state/secrets. \
42See docs/component-developer-guide.md for a walkthrough."
43    )]
44    Test(Box<TestArgs>),
45    /// Flow utilities (config flow regeneration)
46    #[command(subcommand)]
47    Flow(FlowCommand),
48    /// Interact with the component store
49    #[command(subcommand)]
50    Store(StoreCommand),
51}
52
53pub fn main() -> Result<()> {
54    let cli = Cli::parse();
55    let engine = ScaffoldEngine::new();
56    match cli.command {
57        Commands::New(args) => cmd::new::run(args, &engine),
58        Commands::Templates(args) => cmd::templates::run(args, &engine),
59        Commands::Doctor(args) => cmd::doctor::run(args).map_err(Error::new),
60        Commands::Inspect(args) => {
61            let result = cmd::inspect::run(&args)?;
62            cmd::inspect::emit_warnings(&result.warnings);
63            if args.strict && !result.warnings.is_empty() {
64                bail!(
65                    "component-inspect: {} warning(s) treated as errors (--strict)",
66                    result.warnings.len()
67                );
68            }
69            Ok(())
70        }
71        Commands::Hash(args) => cmd::hash::run(args),
72        Commands::Build(args) => cmd::build::run(args),
73        Commands::Test(args) => cmd::test::run(*args),
74        Commands::Flow(flow_cmd) => cmd::flow::run(flow_cmd),
75        Commands::Store(store_cmd) => cmd::store::run(store_cmd),
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn parses_new_subcommand() {
85        let cli = Cli::try_parse_from(["greentic-component", "new", "--name", "demo", "--json"])
86            .expect("expected CLI to parse");
87        match cli.command {
88            Commands::New(args) => {
89                assert_eq!(args.name, "demo");
90                assert!(args.json);
91                assert!(!args.no_check);
92                assert!(!args.no_git);
93            }
94            _ => panic!("expected new args"),
95        }
96    }
97}