greentic_component/cmd/
inspect.rs

1use clap::{Args, Parser};
2use serde_json::Value;
3
4use crate::{ComponentError, PreparedComponent, prepare_component};
5
6#[derive(Args, Debug, Clone)]
7#[command(about = "Inspect a Greentic component artifact")]
8pub struct InspectArgs {
9    /// Path or identifier resolvable by the loader
10    pub target: String,
11    /// Emit structured JSON instead of human output
12    #[arg(long)]
13    pub json: bool,
14    /// Treat warnings as errors
15    #[arg(long)]
16    pub strict: bool,
17}
18
19#[derive(Parser, Debug)]
20struct InspectCli {
21    #[command(flatten)]
22    args: InspectArgs,
23}
24
25pub fn parse_from_cli() -> InspectArgs {
26    InspectCli::parse().args
27}
28
29#[derive(Default)]
30pub struct InspectResult {
31    pub warnings: Vec<String>,
32}
33
34pub fn run(args: &InspectArgs) -> Result<InspectResult, ComponentError> {
35    let prepared = prepare_component(&args.target)?;
36    if args.json {
37        let json = serde_json::to_string_pretty(&build_report(&prepared))
38            .expect("serializing inspect report");
39        println!("{json}");
40    } else {
41        println!("component: {}", prepared.manifest.id.as_str());
42        println!("  wasm: {}", prepared.wasm_path.display());
43        println!("  world ok: {}", prepared.world_ok);
44        println!("  hash: {}", prepared.wasm_hash);
45        println!(
46            "  lifecycle: init={} health={} shutdown={}",
47            prepared.lifecycle.init, prepared.lifecycle.health, prepared.lifecycle.shutdown
48        );
49        println!("  capabilities: {:?}", prepared.manifest.capabilities);
50        println!(
51            "  limits: {}",
52            prepared
53                .manifest
54                .limits
55                .as_ref()
56                .map(|l| format!("{} MB / {} ms", l.memory_mb, l.wall_time_ms))
57                .unwrap_or_else(|| "default".into())
58        );
59        println!(
60            "  telemetry prefix: {}",
61            prepared
62                .manifest
63                .telemetry
64                .as_ref()
65                .map(|t| t.span_prefix.as_str())
66                .unwrap_or("<none>")
67        );
68        println!("  describe versions: {}", prepared.describe.versions.len());
69        println!("  redaction paths: {}", prepared.redaction_paths().len());
70        println!("  defaults applied: {}", prepared.defaults_applied().len());
71    }
72    Ok(InspectResult::default())
73}
74
75pub fn emit_warnings(warnings: &[String]) {
76    for warning in warnings {
77        eprintln!("warning: {warning}");
78    }
79}
80
81pub fn build_report(prepared: &PreparedComponent) -> Value {
82    serde_json::json!({
83        "manifest": &prepared.manifest,
84        "manifest_path": prepared.manifest_path,
85        "wasm_path": prepared.wasm_path,
86        "wasm_hash": prepared.wasm_hash,
87        "hash_verified": prepared.hash_verified,
88        "world": {
89            "expected": prepared.manifest.world.as_str(),
90            "ok": prepared.world_ok,
91        },
92        "lifecycle": {
93            "init": prepared.lifecycle.init,
94            "health": prepared.lifecycle.health,
95            "shutdown": prepared.lifecycle.shutdown,
96        },
97        "describe": prepared.describe,
98        "capabilities": prepared.manifest.capabilities,
99        "limits": prepared.manifest.limits,
100        "telemetry": prepared.manifest.telemetry,
101        "redactions": prepared
102            .redaction_paths()
103            .iter()
104            .map(|p| p.as_str().to_string())
105            .collect::<Vec<_>>(),
106        "defaults_applied": prepared.defaults_applied(),
107        "summary": {
108            "capabilities": {
109                "http": prepared.manifest.capabilities.http.is_some(),
110                "secrets": prepared.manifest.capabilities.secrets.is_some(),
111                "kv": prepared.manifest.capabilities.kv.is_some(),
112                "fs": prepared.manifest.capabilities.fs.is_some(),
113                "net": prepared.manifest.capabilities.net.is_some(),
114                "tools": prepared.manifest.capabilities.tools.is_some(),
115            }
116        }
117    })
118}