ferriorm_codegen/
generator.rs1use ferriorm_core::schema::Schema;
10use ferriorm_core::utils::to_snake_case;
11use std::fs;
12use std::path::Path;
13
14use crate::client::generate_client_module;
15use crate::enums::generate_enums_module;
16use crate::formatter::format_token_stream;
17use crate::model::generate_model_module;
18
19pub fn generate(schema: &Schema, output_dir: &Path) -> Result<(), GenerateError> {
21 fs::create_dir_all(output_dir)
23 .map_err(|e| GenerateError::Io(format!("Failed to create output dir: {e}")))?;
24
25 let mut mod_entries = Vec::new();
26
27 if !schema.enums.is_empty() {
29 let tokens = generate_enums_module(&schema.enums);
30 let code = format_token_stream(tokens);
31 write_file(output_dir, "enums.rs", &code)?;
32 mod_entries.push("pub mod enums;".to_string());
33 }
34
35 for model in &schema.models {
37 let model_tokens = generate_model_module(model);
38 let relation_types = crate::relations::gen_relation_types(model, schema);
39 let relation_include = crate::relations::gen_find_many_include(model, schema);
40 let tokens = quote::quote! {
41 #model_tokens
42 #relation_types
43 #relation_include
44 };
45 let code = format_token_stream(tokens);
46 let filename = format!("{}.rs", to_snake_case(&model.name));
47 write_file(output_dir, &filename, &code)?;
48 mod_entries.push(format!("pub mod {};", to_snake_case(&model.name)));
49 }
50
51 let client_tokens = generate_client_module(schema);
53 let client_code = format_token_stream(client_tokens);
54 write_file(output_dir, "client.rs", &client_code)?;
55 mod_entries.push("pub mod client;".to_string());
56
57 let mut mod_content = String::from("// AUTO-GENERATED by ferriorm. Do not edit.\n\n");
59 for entry in &mod_entries {
60 mod_content.push_str(entry);
61 mod_content.push('\n');
62 }
63 mod_content.push('\n');
64 mod_content.push_str("pub use client::FerriormClient;\n");
65 if !schema.enums.is_empty() {
66 mod_content.push_str("pub use enums::*;\n");
67 }
68
69 let mod_path = output_dir.join("mod.rs");
70 fs::write(&mod_path, mod_content)
71 .map_err(|e| GenerateError::Io(format!("Failed to write {}: {e}", mod_path.display())))?;
72
73 Ok(())
74}
75
76fn write_file(dir: &Path, filename: &str, content: &str) -> Result<(), GenerateError> {
77 let path = dir.join(filename);
78
79 let full_content = format!("// AUTO-GENERATED by ferriorm. Do not edit.\n\n{content}");
81
82 fs::write(&path, full_content)
83 .map_err(|e| GenerateError::Io(format!("Failed to write {}: {e}", path.display())))?;
84
85 Ok(())
86}
87
88#[derive(Debug)]
89pub enum GenerateError {
90 Io(String),
91 CodeGen(String),
92}
93
94impl std::fmt::Display for GenerateError {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::Io(msg) => write!(f, "IO error: {msg}"),
98 Self::CodeGen(msg) => write!(f, "Code generation error: {msg}"),
99 }
100 }
101}
102
103impl std::error::Error for GenerateError {}