1mod error;
24mod mapping;
25mod typescript;
26mod zod;
27
28pub use error::TypegenError;
29pub use mapping::TypeMapper;
30pub use typescript::InterfaceGenerator;
31pub use zod::ZodGenerator;
32
33use std::path::Path;
34
35use prax_schema::{Schema, validate_schema};
36
37pub type Result<T> = std::result::Result<T, TypegenError>;
39
40pub struct Typegen {
42 pub interfaces: bool,
43 pub zod: bool,
44}
45
46impl Default for Typegen {
47 fn default() -> Self {
48 Self {
49 interfaces: true,
50 zod: true,
51 }
52 }
53}
54
55impl Typegen {
56 pub fn new() -> Self {
57 Self::default()
58 }
59
60 pub fn interfaces_only() -> Self {
61 Self {
62 interfaces: true,
63 zod: false,
64 }
65 }
66
67 pub fn zod_only() -> Self {
68 Self {
69 interfaces: false,
70 zod: true,
71 }
72 }
73
74 pub fn generate_from_file(&self, schema_path: &Path) -> Result<GeneratedOutput> {
76 let input = std::fs::read_to_string(schema_path).map_err(|e| {
77 TypegenError::Io(format!("failed to read {}: {e}", schema_path.display()))
78 })?;
79 self.generate_from_str(&input)
80 }
81
82 pub fn generate_from_str(&self, input: &str) -> Result<GeneratedOutput> {
84 let schema = validate_schema(input).map_err(|e| TypegenError::Schema(e.to_string()))?;
85 self.generate(&schema)
86 }
87
88 pub fn generate(&self, schema: &Schema) -> Result<GeneratedOutput> {
90 let mut output = GeneratedOutput::default();
91
92 if self.interfaces {
93 output.interfaces = InterfaceGenerator::generate(schema);
94 }
95 if self.zod {
96 output.zod = ZodGenerator::generate(schema);
97 }
98
99 Ok(output)
100 }
101
102 pub fn write_to_dir(&self, schema: &Schema, out_dir: &Path) -> Result<Vec<String>> {
104 std::fs::create_dir_all(out_dir).map_err(|e| {
105 TypegenError::Io(format!(
106 "failed to create output dir {}: {e}",
107 out_dir.display()
108 ))
109 })?;
110
111 let output = self.generate(schema)?;
112 let mut written = Vec::new();
113
114 if self.interfaces && !output.interfaces.is_empty() {
115 let path = out_dir.join("models.ts");
116 std::fs::write(&path, &output.interfaces).map_err(|e| {
117 TypegenError::Io(format!("failed to write {}: {e}", path.display()))
118 })?;
119 written.push(path.display().to_string());
120 }
121
122 if self.zod && !output.zod.is_empty() {
123 let path = out_dir.join("schemas.ts");
124 std::fs::write(&path, &output.zod).map_err(|e| {
125 TypegenError::Io(format!("failed to write {}: {e}", path.display()))
126 })?;
127 written.push(path.display().to_string());
128 }
129
130 if self.interfaces || self.zod {
131 let index = build_index(self.interfaces, self.zod);
132 let path = out_dir.join("index.ts");
133 std::fs::write(&path, index).map_err(|e| {
134 TypegenError::Io(format!("failed to write {}: {e}", path.display()))
135 })?;
136 written.push(path.display().to_string());
137 }
138
139 Ok(written)
140 }
141}
142
143#[derive(Debug, Default)]
145pub struct GeneratedOutput {
146 pub interfaces: String,
148 pub zod: String,
150}
151
152fn build_index(interfaces: bool, zod: bool) -> String {
153 let mut lines = vec![
154 "// Auto-generated by prax-typegen. Do not edit.".to_string(),
155 String::new(),
156 ];
157 if interfaces {
158 lines.push("export * from './models';".to_string());
159 }
160 if zod {
161 lines.push("export * from './schemas';".to_string());
162 }
163 lines.push(String::new());
164 lines.join("\n")
165}
166
167pub fn generator_enabled(schema: &Schema, name: &str) -> bool {
169 schema.get_generator(name).is_some_and(|g| g.is_enabled())
170}
171
172pub fn resolve_output_dir(schema: &Schema, generator_name: &str, fallback: &str) -> String {
174 schema
175 .get_generator(generator_name)
176 .and_then(|g| g.output.as_ref())
177 .map(|s| s.to_string())
178 .unwrap_or_else(|| fallback.to_string())
179}