mold_cli/generators/prisma/
mod.rs1mod relations;
2mod types;
3
4use crate::generators::{Generator, GeneratorConfig};
5use crate::types::{NestedType, ObjectType, Schema, SchemaType};
6use anyhow::Result;
7use std::collections::HashMap;
8
9use relations::{format_model_name, generate_field};
10
11pub struct PrismaGenerator;
12
13impl PrismaGenerator {
14 pub fn new() -> Self {
15 Self
16 }
17
18 fn generate_model(
19 &self,
20 name: &str,
21 obj: &ObjectType,
22 indent: &str,
23 type_refs: &HashMap<String, String>,
24 generate_relations: bool,
25 ) -> String {
26 let model_name = format_model_name(name);
27 let mut lines = vec![format!("model {} {{", model_name)];
28
29 lines.push(format!("{}id Int @id @default(autoincrement())", indent));
30
31 for field in &obj.fields {
32 if let Some(field_lines) = generate_field(field, indent, type_refs, generate_relations) {
33 for line in field_lines {
34 lines.push(line);
35 }
36 }
37 }
38
39 lines.push("}".to_string());
40 lines.join("\n")
41 }
42
43 fn build_type_refs(&self, nested_types: &[NestedType]) -> HashMap<String, String> {
44 let mut refs = HashMap::new();
45 for nt in nested_types {
46 let key = format!("{:?}", nt.object);
47 refs.insert(key, nt.name.clone());
48 }
49 refs
50 }
51
52 fn generate_nested_models(
53 &self,
54 nested_types: &[NestedType],
55 indent: &str,
56 type_refs: &HashMap<String, String>,
57 generate_relations: bool,
58 ) -> Vec<String> {
59 nested_types
60 .iter()
61 .rev()
62 .map(|nt| self.generate_model(&nt.name, &nt.object, indent, type_refs, generate_relations))
63 .collect()
64 }
65}
66
67impl Default for PrismaGenerator {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl Generator for PrismaGenerator {
74 fn generate(&self, schema: &Schema, config: &GeneratorConfig) -> Result<String> {
75 let mut output = vec!["// Generated by mold".to_string(), String::new()];
76
77 let type_refs = if config.flat_mode {
78 HashMap::new()
79 } else {
80 self.build_type_refs(&schema.nested_types)
81 };
82
83 if !config.flat_mode && !schema.nested_types.is_empty() {
84 let nested = self.generate_nested_models(
85 &schema.nested_types,
86 &config.indent,
87 &type_refs,
88 config.prisma_generate_relations,
89 );
90 for model in nested {
91 output.push(model);
92 output.push(String::new());
93 }
94 }
95
96 if let SchemaType::Object(obj) = &schema.root_type {
97 output.push(self.generate_model(
98 &schema.name,
99 obj,
100 &config.indent,
101 &type_refs,
102 config.prisma_generate_relations,
103 ));
104 }
105
106 Ok(output.join("\n"))
107 }
108
109 fn file_extension(&self) -> &'static str {
110 "prisma"
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::types::Field;
118
119 #[test]
120 fn test_generate_simple_model() {
121 let gen = PrismaGenerator::new();
122 let obj = ObjectType::new(vec![
123 Field::new("name", SchemaType::String),
124 Field::new("age", SchemaType::Integer),
125 Field::new("active", SchemaType::Boolean),
126 ]);
127 let schema = Schema::new("User", SchemaType::Object(obj));
128 let config = GeneratorConfig::default();
129
130 let output = gen.generate(&schema, &config).unwrap();
131
132 assert!(output.contains("model User {"));
133 assert!(output.contains("id Int @id @default(autoincrement())"));
134 assert!(output.contains("name String"));
135 assert!(output.contains("age Int"));
136 assert!(output.contains("active Boolean"));
137 }
138
139 #[test]
140 fn test_generate_array_field() {
141 let gen = PrismaGenerator::new();
142 let obj = ObjectType::new(vec![Field::new(
143 "tags",
144 SchemaType::Array(Box::new(SchemaType::String)),
145 )]);
146 let schema = Schema::new("Post", SchemaType::Object(obj));
147 let config = GeneratorConfig::default();
148
149 let output = gen.generate(&schema, &config).unwrap();
150
151 assert!(output.contains("tags String[]"));
152 }
153
154 #[test]
155 fn test_generate_float_field() {
156 let gen = PrismaGenerator::new();
157 let obj = ObjectType::new(vec![Field::new("price", SchemaType::Number)]);
158 let schema = Schema::new("Product", SchemaType::Object(obj));
159 let config = GeneratorConfig::default();
160
161 let output = gen.generate(&schema, &config).unwrap();
162
163 assert!(output.contains("price Float"));
164 }
165
166 #[test]
167 fn test_reserved_word_handling() {
168 let gen = PrismaGenerator::new();
169 let obj = ObjectType::new(vec![Field::new("model", SchemaType::String)]);
170 let schema = Schema::new("Test", SchemaType::Object(obj));
171 let config = GeneratorConfig::default();
172
173 let output = gen.generate(&schema, &config).unwrap();
174
175 assert!(output.contains("model_ String"));
176 }
177}