greentic_component/cmd/
inspect.rs1use 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 pub target: String,
11 #[arg(long)]
13 pub json: bool,
14 #[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}