vika_cli/commands/
inspect.rs

1use crate::error::Result;
2use crate::generator::swagger_parser::fetch_and_parse_spec;
3use colored::*;
4use tabled::{Table, Tabled};
5
6#[derive(Tabled)]
7struct ModuleInfo {
8    #[tabled(rename = "Module")]
9    module: String,
10    #[tabled(rename = "Endpoints")]
11    endpoints: usize,
12    #[tabled(rename = "Schemas")]
13    schemas: usize,
14}
15
16pub async fn run(
17    spec: Option<String>,
18    all_specs: bool,
19    spec_name: Option<String>,
20    module: Option<String>,
21    schemas: bool,
22    _graph: bool,
23    json: bool,
24) -> Result<()> {
25    use crate::error::GenerationError;
26    use crate::specs::manager::{get_spec_by_name, list_specs};
27
28    // Load config
29    let config = crate::config::loader::load_config()?;
30    crate::config::validator::validate_config(&config)?;
31
32    // Get specs from config
33    let specs = list_specs(&config);
34
35    if specs.is_empty() {
36        return Err(GenerationError::SpecPathRequired.into());
37    }
38
39    // Handle multiple specs or single spec
40    if specs.len() > 1 || all_specs {
41        if all_specs {
42            // Inspect all specs
43
44            if json {
45                // JSON output for all specs
46                let mut all_specs_data = Vec::new();
47                for spec_entry in &specs {
48                    let parsed =
49                        crate::generator::swagger_parser::fetch_and_parse_spec(&spec_entry.path)
50                            .await?;
51                    all_specs_data.push(serde_json::json!({
52                        "spec_name": spec_entry.name,
53                        "spec_path": spec_entry.path,
54                        "modules": parsed.modules.len(),
55                        "total_endpoints": parsed.operations_by_tag.values().map(|v| v.len()).sum::<usize>(),
56                        "total_schemas": parsed.schemas.len(),
57                        "modules_detail": parsed.modules.iter().map(|m| {
58                            let ops = parsed.operations_by_tag.get(m).map(|v| v.len()).unwrap_or(0);
59                            let schemas_count = parsed.module_schemas.get(m).map(|v| v.len()).unwrap_or(0);
60                            serde_json::json!({
61                                "module": m,
62                                "endpoints": ops,
63                                "schemas": schemas_count
64                            })
65                        }).collect::<Vec<_>>()
66                    }));
67                }
68                println!(
69                    "{}",
70                    serde_json::to_string_pretty(&serde_json::json!({
71                        "specs": all_specs_data
72                    }))
73                    .map_err(|e| {
74                        GenerationError::InvalidOperation {
75                            message: format!("Failed to serialize JSON: {}", e),
76                        }
77                    })?
78                );
79            } else {
80                // Human-readable output for all specs
81                println!("{}", "🔍 Inspecting all OpenAPI specs...".bright_cyan());
82                println!();
83
84                for spec_entry in &specs {
85                    println!("{}", format!("📋 Spec: {}", spec_entry.name).bright_green());
86                    println!("  Path: {}", spec_entry.path);
87                    let parsed =
88                        crate::generator::swagger_parser::fetch_and_parse_spec(&spec_entry.path)
89                            .await?;
90                    println!("  • Total modules: {}", parsed.modules.len());
91                    println!(
92                        "  • Total endpoints: {}",
93                        parsed
94                            .operations_by_tag
95                            .values()
96                            .map(|v| v.len())
97                            .sum::<usize>()
98                    );
99                    println!("  • Total schemas: {}", parsed.schemas.len());
100                    println!();
101                }
102            }
103            return Ok(());
104        } else if let Some(name) = spec_name {
105            // Inspect specific spec by name
106            let spec_entry = get_spec_by_name(&config, &name)?;
107            let spec_path = spec_entry.path;
108            let parsed = crate::generator::swagger_parser::fetch_and_parse_spec(&spec_path).await?;
109
110            if json {
111                let output = serde_json::json!({
112                    "spec_name": name,
113                    "spec_path": spec_path,
114                    "modules": parsed.modules.len(),
115                    "total_endpoints": parsed.operations_by_tag.values().map(|v| v.len()).sum::<usize>(),
116                    "total_schemas": parsed.schemas.len(),
117                    "modules_detail": parsed.modules.iter().map(|m| {
118                        let ops = parsed.operations_by_tag.get(m).map(|v| v.len()).unwrap_or(0);
119                        let schemas_count = parsed.module_schemas.get(m).map(|v| v.len()).unwrap_or(0);
120                        serde_json::json!({
121                            "module": m,
122                            "endpoints": ops,
123                            "schemas": schemas_count
124                        })
125                    }).collect::<Vec<_>>()
126                });
127                println!(
128                    "{}",
129                    serde_json::to_string_pretty(&output).map_err(|e| {
130                        GenerationError::InvalidOperation {
131                            message: format!("Failed to serialize JSON: {}", e),
132                        }
133                    })?
134                );
135            } else {
136                println!(
137                    "{}",
138                    format!("🔍 Inspecting OpenAPI spec: {}", name).bright_cyan()
139                );
140                println!("  Path: {}", spec_path);
141                println!();
142                println!("{}", "📊 Spec Summary:".bright_cyan());
143                println!("  • Total modules: {}", parsed.modules.len());
144                println!(
145                    "  • Total endpoints: {}",
146                    parsed
147                        .operations_by_tag
148                        .values()
149                        .map(|v| v.len())
150                        .sum::<usize>()
151                );
152                println!("  • Total schemas: {}", parsed.schemas.len());
153                println!();
154
155                if let Some(module_name) = module {
156                    // Show details for specific module
157                    if let Some(operations) = parsed.operations_by_tag.get(&module_name) {
158                        println!("{}", format!("📦 Module: {}", module_name).bright_green());
159                        println!("  • Endpoints: {}", operations.len());
160                        if let Some(schema_names) = parsed.module_schemas.get(&module_name) {
161                            println!("  • Schemas: {}", schema_names.len());
162                            if schemas {
163                                println!("  • Schema names:");
164                                for schema in schema_names {
165                                    println!("    - {}", schema);
166                                }
167                            }
168                        }
169                    } else {
170                        println!(
171                            "{}",
172                            format!("⚠️  Module '{}' not found", module_name).yellow()
173                        );
174                    }
175                } else {
176                    // Show all modules
177                    let table_data: Vec<ModuleInfo> = parsed
178                        .modules
179                        .iter()
180                        .map(|m| {
181                            let endpoints = parsed
182                                .operations_by_tag
183                                .get(m)
184                                .map(|v| v.len())
185                                .unwrap_or(0);
186                            let schemas_count =
187                                parsed.module_schemas.get(m).map(|v| v.len()).unwrap_or(0);
188                            ModuleInfo {
189                                module: m.clone(),
190                                endpoints,
191                                schemas: schemas_count,
192                            }
193                        })
194                        .collect();
195
196                    let table = Table::new(table_data);
197                    println!("{}", "📦 Modules:".bright_cyan());
198                    println!("{}", table);
199                }
200            }
201            return Ok(());
202        } else {
203            // Multi-spec mode but no flag: prompt user or show all
204            let specs = list_specs(&config);
205            if specs.is_empty() {
206                return Err(GenerationError::SpecPathRequired.into());
207            }
208
209            // Default to showing all specs summary
210            println!("{}", "🔍 Inspecting all OpenAPI specs...".bright_cyan());
211            println!();
212
213            for spec_entry in &specs {
214                println!("{}", format!("📋 Spec: {}", spec_entry.name).bright_green());
215                println!("  Path: {}", spec_entry.path);
216                let parsed =
217                    crate::generator::swagger_parser::fetch_and_parse_spec(&spec_entry.path)
218                        .await?;
219                println!("  • Total modules: {}", parsed.modules.len());
220                println!(
221                    "  • Total endpoints: {}",
222                    parsed
223                        .operations_by_tag
224                        .values()
225                        .map(|v| v.len())
226                        .sum::<usize>()
227                );
228                println!("  • Total schemas: {}", parsed.schemas.len());
229                println!();
230            }
231            return Ok(());
232        }
233    }
234
235    // Single spec mode - use first spec
236    let spec_entry = if let Some(name) = spec_name {
237        get_spec_by_name(&config, &name)?
238    } else if let Some(cli_spec) = spec {
239        // CLI spec argument - try to find by name or use as path
240        get_spec_by_name(&config, &cli_spec).unwrap_or_else(|_| {
241            // If not found by name, treat as path and find matching spec
242            specs
243                .iter()
244                .find(|s| s.path == cli_spec)
245                .cloned()
246                .ok_or(GenerationError::SpecPathRequired)
247                .unwrap()
248        })
249    } else {
250        // Use first spec
251        specs[0].clone()
252    };
253
254    let spec_path = &spec_entry.path;
255    println!(
256        "{}",
257        format!("🔍 Inspecting OpenAPI spec: {}", spec_entry.name).bright_cyan()
258    );
259    println!();
260
261    let parsed = fetch_and_parse_spec(spec_path).await?;
262
263    if json {
264        // JSON output
265        let output = serde_json::json!({
266            "modules": parsed.modules.len(),
267            "total_endpoints": parsed.operations_by_tag.values().map(|v| v.len()).sum::<usize>(),
268            "total_schemas": parsed.schemas.len(),
269            "modules_detail": parsed.modules.iter().map(|m| {
270                let ops = parsed.operations_by_tag.get(m).map(|v| v.len()).unwrap_or(0);
271                let schemas_count = parsed.module_schemas.get(m).map(|v| v.len()).unwrap_or(0);
272                serde_json::json!({
273                    "module": m,
274                    "endpoints": ops,
275                    "schemas": schemas_count
276                })
277            }).collect::<Vec<_>>()
278        });
279        println!(
280            "{}",
281            serde_json::to_string_pretty(&output).map_err(|e| {
282                crate::error::GenerationError::InvalidOperation {
283                    message: format!("Failed to serialize JSON: {}", e),
284                }
285            })?
286        );
287    } else {
288        // Human-readable output
289        println!("{}", "📊 Spec Summary:".bright_cyan());
290        println!("  • Total modules: {}", parsed.modules.len());
291        println!(
292            "  • Total endpoints: {}",
293            parsed
294                .operations_by_tag
295                .values()
296                .map(|v| v.len())
297                .sum::<usize>()
298        );
299        println!("  • Total schemas: {}", parsed.schemas.len());
300        println!();
301
302        if let Some(module_name) = module {
303            // Show details for specific module
304            if let Some(operations) = parsed.operations_by_tag.get(&module_name) {
305                println!("{}", format!("📦 Module: {}", module_name).bright_green());
306                println!("  • Endpoints: {}", operations.len());
307                if let Some(schema_names) = parsed.module_schemas.get(&module_name) {
308                    println!("  • Schemas: {}", schema_names.len());
309                    if schemas {
310                        println!("  • Schema names:");
311                        for schema in schema_names {
312                            println!("    - {}", schema);
313                        }
314                    }
315                }
316            } else {
317                println!(
318                    "{}",
319                    format!("⚠️  Module '{}' not found", module_name).yellow()
320                );
321            }
322        } else {
323            // Show all modules
324            let table_data: Vec<ModuleInfo> = parsed
325                .modules
326                .iter()
327                .map(|m| {
328                    let endpoints = parsed
329                        .operations_by_tag
330                        .get(m)
331                        .map(|v| v.len())
332                        .unwrap_or(0);
333                    let schemas_count = parsed.module_schemas.get(m).map(|v| v.len()).unwrap_or(0);
334                    ModuleInfo {
335                        module: m.clone(),
336                        endpoints,
337                        schemas: schemas_count,
338                    }
339                })
340                .collect();
341
342            let table = Table::new(table_data);
343            println!("{}", "📦 Modules:".bright_cyan());
344            println!("{}", table);
345        }
346    }
347
348    Ok(())
349}