Skip to main content

torvyn_cli/commands/
inspect.rs

1//! `torvyn inspect` — inspect a component or artifact.
2
3use crate::errors::CliError;
4use crate::output::terminal;
5use crate::output::{CommandResult, HumanRenderable, OutputContext};
6use serde::Serialize;
7use std::path::Path;
8
9/// Result of `torvyn inspect`.
10#[derive(Debug, Serialize)]
11pub struct InspectResult {
12    /// Component name.
13    pub name: String,
14    /// Component version.
15    pub version: String,
16    /// Wasm binary size.
17    pub size_wasm_bytes: u64,
18    /// Packaged size (if inspecting an artifact).
19    pub size_packaged_bytes: Option<u64>,
20    /// Exported interfaces.
21    pub exports: Vec<String>,
22    /// Imported interfaces.
23    pub imports: Vec<String>,
24    /// Required capabilities.
25    pub capabilities_required: Vec<String>,
26    /// Contract version.
27    pub contract_version: Option<String>,
28    /// Build tool info.
29    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
83/// Execute the `torvyn inspect` command.
84///
85/// COLD PATH.
86pub 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    // Check if this is a packaged artifact (.torvyn extension)
110    let is_artifact = target_path
111        .extension()
112        .map(|ext| ext == "torvyn" || ext == "tar")
113        .unwrap_or(false);
114
115    if is_artifact {
116        // Delegate to torvyn-packaging for artifact inspection
117        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                // Fall through to basic file inspection
148            }
149        }
150    }
151
152    // Basic file inspection (for .wasm files or fallback)
153    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}