Skip to main content

fraiseql_cli/commands/generate/
mod.rs

1//! `fraiseql generate` — Generate authoring-language source from schema.json
2//!
3//! The inverse of `fraiseql extract`: reads a schema.json and produces annotated
4//! source code in any of the 9 supported authoring languages.
5
6use std::fs;
7
8use anyhow::{Context, Result};
9use tracing::info;
10
11use super::init::Language;
12use crate::schema::intermediate::IntermediateSchema;
13
14mod csharp;
15mod go_lang;
16mod java;
17mod kotlin;
18mod php;
19mod python;
20mod rust_lang;
21mod scala;
22mod swift;
23mod typescript;
24mod utils;
25
26#[cfg(test)]
27mod tests;
28
29// =============================================================================
30// Trait
31// =============================================================================
32
33/// Trait for language-specific code generation from intermediate schema.
34pub(super) trait SchemaGenerator {
35    fn generate(&self, schema: &IntermediateSchema) -> String;
36}
37
38// =============================================================================
39// Public API
40// =============================================================================
41
42/// Run the generate command.
43///
44/// # Errors
45///
46/// Returns an error if the input schema file cannot be read or parsed as
47/// `IntermediateSchema`, or if the generated output file cannot be written.
48pub fn run(input: &str, language: Language, output: Option<&str>) -> Result<()> {
49    let json = fs::read_to_string(input).with_context(|| format!("Failed to read {input}"))?;
50    let schema: IntermediateSchema =
51        serde_json::from_str(&json).with_context(|| format!("Failed to parse {input}"))?;
52
53    let code = dispatch_generator(language, &schema);
54
55    let out_path = match output {
56        Some(p) => p.to_string(),
57        None => default_output_path(language),
58    };
59
60    fs::write(&out_path, &code).with_context(|| format!("Failed to write {out_path}"))?;
61
62    let type_count = schema.types.len();
63    let query_count = schema.queries.len();
64    let enum_count = schema.enums.len();
65    info!("Generated {type_count} types, {query_count} queries, {enum_count} enums");
66    println!(
67        "Generated {type_count} types, {query_count} queries, {enum_count} enums → {out_path}",
68    );
69
70    Ok(())
71}
72
73fn default_output_path(lang: Language) -> String {
74    match lang {
75        Language::Python => "schema.py".to_string(),
76        Language::TypeScript => "schema.ts".to_string(),
77        Language::Rust => "schema.rs".to_string(),
78        Language::Java => "Schema.java".to_string(),
79        Language::Kotlin => "Schema.kt".to_string(),
80        Language::Go => "schema.go".to_string(),
81        Language::CSharp => "Schema.cs".to_string(),
82        Language::Swift => "Schema.swift".to_string(),
83        Language::Scala => "Schema.scala".to_string(),
84        Language::Php => "schema.php".to_string(),
85    }
86}
87
88fn dispatch_generator(lang: Language, schema: &IntermediateSchema) -> String {
89    match lang {
90        Language::Python => python::PythonGenerator.generate(schema),
91        Language::TypeScript => typescript::TypeScriptGenerator.generate(schema),
92        Language::Rust => rust_lang::RustGenerator.generate(schema),
93        Language::Kotlin => kotlin::KotlinGenerator.generate(schema),
94        Language::Swift => swift::SwiftGenerator.generate(schema),
95        Language::Scala => scala::ScalaGenerator.generate(schema),
96        Language::Java => java::JavaGenerator.generate(schema),
97        Language::Go => go_lang::GoGenerator.generate(schema),
98        Language::CSharp => csharp::CSharpGenerator.generate(schema),
99        Language::Php => php::PhpGenerator.generate(schema),
100    }
101}