Skip to main content

spikard_cli/
app.rs

1//! Shared application layer for CLI and MCP operations.
2
3use crate::codegen::{
4    CodegenEngine, CodegenOutcome, CodegenRequest, GeneratedAsset, PhpDtoGenerator, detect_primary_protocol,
5    parse_asyncapi_schema,
6};
7use crate::init::{InitEngine, InitRequest, InitResponse};
8use anyhow::{Context, Result};
9use serde::Serialize;
10use std::fs;
11use std::path::Path;
12
13/// Result of AsyncAPI validation exposed to both CLI and MCP surfaces.
14#[derive(Debug, Clone, Serialize)]
15pub struct AsyncApiValidationSummary {
16    pub spec_version: String,
17    pub title: String,
18    pub api_version: String,
19    pub primary_protocol: String,
20    pub channel_count: usize,
21}
22
23/// Human-readable framework capability summary for agents and CLI users.
24#[derive(Debug, Clone, Serialize)]
25pub struct FeatureSummary {
26    pub rust_core: bool,
27    pub language_bindings: Vec<LanguageBinding>,
28    pub documentation_url: String,
29}
30
31/// Install and usage hint for a supported binding.
32#[derive(Debug, Clone, Serialize)]
33pub struct LanguageBinding {
34    pub name: String,
35    pub install_hint: String,
36    pub usage_hint: String,
37}
38
39/// Execute a project initialization request.
40pub fn init_project(request: InitRequest) -> Result<InitResponse> {
41    InitEngine::execute(request)
42}
43
44/// Execute validated code generation.
45pub fn execute_codegen(request: CodegenRequest) -> Result<CodegenOutcome> {
46    CodegenEngine::execute_validated(request)
47}
48
49/// Execute code generation without validator passes.
50pub fn execute_codegen_unvalidated(request: CodegenRequest) -> Result<CodegenOutcome> {
51    CodegenEngine::execute(request)
52}
53
54/// Generate the PHP DTO helper classes.
55pub fn generate_php_dto(output: &Path) -> Result<Vec<GeneratedAsset>> {
56    let generator = PhpDtoGenerator::new();
57    let generated = generator.generate_all().context("Failed to generate PHP DTOs")?;
58
59    fs::create_dir_all(output).with_context(|| format!("Failed to create output directory: {}", output.display()))?;
60
61    let mut assets = Vec::with_capacity(generated.len());
62    for (filename, code) in generated {
63        let file_path = output.join(&filename);
64        fs::write(&file_path, code).with_context(|| format!("Failed to write DTO file: {}", file_path.display()))?;
65        assets.push(GeneratedAsset {
66            path: file_path,
67            description: format!("PHP DTO class {}", filename),
68        });
69    }
70
71    Ok(assets)
72}
73
74/// Validate an AsyncAPI schema and return the structured summary.
75pub fn validate_asyncapi_schema(schema: &Path) -> Result<AsyncApiValidationSummary> {
76    let spec = parse_asyncapi_schema(schema).context("Failed to parse AsyncAPI schema")?;
77    let protocol = detect_primary_protocol(&spec)?;
78
79    Ok(AsyncApiValidationSummary {
80        spec_version: "3.0.0".to_string(),
81        title: spec.info.title,
82        api_version: spec.info.version,
83        primary_protocol: format!("{protocol:?}"),
84        channel_count: spec.channels.len(),
85    })
86}
87
88/// Return the current feature summary shown by the CLI and exposed via MCP.
89#[must_use]
90pub fn feature_summary() -> FeatureSummary {
91    FeatureSummary {
92        rust_core: true,
93        language_bindings: vec![
94            LanguageBinding {
95                name: "Rust".to_string(),
96                install_hint: "cargo add spikard".to_string(),
97                usage_hint: "cargo run".to_string(),
98            },
99            LanguageBinding {
100                name: "Python".to_string(),
101                install_hint: "pip install spikard".to_string(),
102                usage_hint: "python server.py".to_string(),
103            },
104            LanguageBinding {
105                name: "TypeScript".to_string(),
106                install_hint: "npm install spikard".to_string(),
107                usage_hint: "node server.js".to_string(),
108            },
109            LanguageBinding {
110                name: "Ruby".to_string(),
111                install_hint: "gem install spikard".to_string(),
112                usage_hint: "ruby app.rb".to_string(),
113            },
114            LanguageBinding {
115                name: "PHP".to_string(),
116                install_hint: "composer require spikard/spikard".to_string(),
117                usage_hint: "php src/App.php".to_string(),
118            },
119            LanguageBinding {
120                name: "Elixir".to_string(),
121                install_hint: "mix deps.get".to_string(),
122                usage_hint: "iex -S mix".to_string(),
123            },
124        ],
125        documentation_url: "https://spikard.dev".to_string(),
126    }
127}