torvyn_cli/commands/
inspect.rs1use crate::errors::CliError;
4use crate::output::terminal;
5use crate::output::{CommandResult, HumanRenderable, OutputContext};
6use serde::Serialize;
7use std::path::Path;
8
9#[derive(Debug, Serialize)]
11pub struct InspectResult {
12 pub name: String,
14 pub version: String,
16 pub size_wasm_bytes: u64,
18 pub size_packaged_bytes: Option<u64>,
20 pub exports: Vec<String>,
22 pub imports: Vec<String>,
24 pub capabilities_required: Vec<String>,
26 pub contract_version: Option<String>,
28 pub build_info: Option<String>,
30}
31
32impl HumanRenderable for InspectResult {
33 fn render_human(&self, ctx: &OutputContext) {
34 terminal::print_kv(ctx, "Component", &self.name);
35 terminal::print_kv(ctx, "Version", &self.version);
36 terminal::print_kv(
37 ctx,
38 "Size",
39 &format!(
40 "{} (Wasm){}",
41 terminal::format_bytes(self.size_wasm_bytes),
42 self.size_packaged_bytes
43 .map(|s| format!(", {} (packaged)", terminal::format_bytes(s)))
44 .unwrap_or_default()
45 ),
46 );
47
48 if !self.exports.is_empty() {
49 eprintln!();
50 eprintln!(" Exports:");
51 for exp in &self.exports {
52 eprintln!(" {exp}");
53 }
54 }
55
56 if !self.imports.is_empty() {
57 eprintln!();
58 eprintln!(" Imports:");
59 for imp in &self.imports {
60 eprintln!(" {imp}");
61 }
62 }
63
64 eprintln!();
65 if self.capabilities_required.is_empty() {
66 eprintln!(" Capabilities required: (none)");
67 } else {
68 eprintln!(" Capabilities required:");
69 for cap in &self.capabilities_required {
70 eprintln!(" {cap}");
71 }
72 }
73
74 if let Some(cv) = &self.contract_version {
75 terminal::print_kv(ctx, "Contract version", cv);
76 }
77 if let Some(bi) = &self.build_info {
78 terminal::print_kv(ctx, "Built with", bi);
79 }
80 }
81}
82
83pub async fn execute(
87 args: &crate::cli::InspectArgs,
88 _ctx: &OutputContext,
89) -> Result<CommandResult<InspectResult>, CliError> {
90 let target = &args.target;
91 let target_path = Path::new(target);
92
93 if !target_path.exists() {
94 return Err(CliError::Config {
95 detail: format!("Target not found: {target}"),
96 file: Some(target.clone()),
97 suggestion: "Provide a path to a .wasm file or packaged artifact.".into(),
98 });
99 }
100
101 let file_size = std::fs::metadata(target_path).map(|m| m.len()).unwrap_or(0);
102
103 let name = target_path
104 .file_stem()
105 .and_then(|s| s.to_str())
106 .unwrap_or("unknown")
107 .to_string();
108
109 let is_artifact = target_path
111 .extension()
112 .map(|ext| ext == "torvyn" || ext == "tar")
113 .unwrap_or(false);
114
115 if is_artifact {
116 match torvyn_packaging::inspect(target_path) {
118 Ok(inspection) => {
119 let result = InspectResult {
120 name: inspection.name,
121 version: inspection.version,
122 size_wasm_bytes: inspection.wasm_size_bytes as u64,
123 size_packaged_bytes: Some(file_size),
124 exports: vec![],
125 imports: vec![],
126 capabilities_required: inspection.capabilities_required,
127 contract_version: if inspection.min_torvyn_version.is_empty() {
128 None
129 } else {
130 Some(inspection.min_torvyn_version)
131 },
132 build_info: if inspection.build_tool.is_empty() {
133 None
134 } else {
135 Some(inspection.build_tool)
136 },
137 };
138
139 return Ok(CommandResult {
140 success: true,
141 command: "inspect".into(),
142 data: result,
143 warnings: vec![],
144 });
145 }
146 Err(_e) => {
147 }
149 }
150 }
151
152 let result = InspectResult {
154 name,
155 version: "0.1.0".into(),
156 size_wasm_bytes: file_size,
157 size_packaged_bytes: None,
158 exports: vec![],
159 imports: vec![],
160 capabilities_required: vec![],
161 contract_version: None,
162 build_info: None,
163 };
164
165 Ok(CommandResult {
166 success: true,
167 command: "inspect".into(),
168 data: result,
169 warnings: vec![],
170 })
171}