1use 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#[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#[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#[derive(Debug, Clone, Serialize)]
33pub struct LanguageBinding {
34 pub name: String,
35 pub install_hint: String,
36 pub usage_hint: String,
37}
38
39pub fn init_project(request: InitRequest) -> Result<InitResponse> {
41 InitEngine::execute(request)
42}
43
44pub fn execute_codegen(request: CodegenRequest) -> Result<CodegenOutcome> {
46 CodegenEngine::execute_validated(request)
47}
48
49pub fn execute_codegen_unvalidated(request: CodegenRequest) -> Result<CodegenOutcome> {
51 CodegenEngine::execute(request)
52}
53
54pub 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
74pub 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#[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}