gear_mesh_generator/
validation_gen.rs1use crate::GeneratorConfig;
2use gear_mesh_core::{FieldInfo, GearMeshType, TypeKind};
3
4pub struct ValidationGenerator {
6 config: GeneratorConfig,
7}
8
9impl ValidationGenerator {
10 pub fn new(config: GeneratorConfig) -> Self {
11 Self { config }
12 }
13
14 pub fn generate_zod_schema(&self, ty: &GearMeshType) -> Option<String> {
16 match &ty.kind {
17 TypeKind::Struct(s) => {
18 let mut schema = format!("export const {}Schema = z.object({{\n", ty.name);
19
20 for field in &s.fields {
21 let field_schema = self.field_to_zod(field);
22 schema.push_str(&format!(" {}: {},\n", field.name, field_schema));
23 }
24
25 schema.push_str("});\n");
26 Some(schema)
27 }
28 _ => None,
29 }
30 }
31
32 fn field_to_zod(&self, field: &FieldInfo) -> String {
33 let is_option = field.optional;
34
35 let target_type = if field.ty.name == "Option" && !field.ty.generics.is_empty() {
41 &field.ty.generics[0]
42 } else {
43 &field.ty
44 };
45
46 let base_schema = self.type_to_zod(target_type);
48
49 let is_bigint = self.config.use_bigint && crate::utils::is_bigint_type(&target_type.name);
52
53 let mut result = base_schema;
54
55 for rule in &field.validations {
57 result.push_str(&rule.to_zod_schema(is_bigint));
58 }
59
60 if is_option {
62 result.push_str(".nullable()");
63 }
64
65 result
66 }
67
68 fn type_to_zod(&self, type_ref: &gear_mesh_core::TypeRef) -> String {
70 match type_ref.name.as_str() {
71 name if crate::utils::is_builtin_type(name) => {
73 match name {
75 "Vec" | "Array" => {
76 if !type_ref.generics.is_empty() {
77 let inner_schema = self.type_to_zod(&type_ref.generics[0]);
78 format!("z.array({})", inner_schema)
79 } else {
80 "z.array(z.unknown())".to_string()
81 }
82 }
83 "Option" => {
84 if !type_ref.generics.is_empty() {
85 let inner_schema = self.type_to_zod(&type_ref.generics[0]);
86 if inner_schema.ends_with(".nullable()") {
88 inner_schema
89 } else {
90 format!("{}.nullable()", inner_schema)
91 }
92 } else {
93 "z.unknown().nullable()".to_string()
94 }
95 }
96 "HashMap" | "BTreeMap" => {
97 let value_schema = if type_ref.generics.len() >= 2 {
98 self.type_to_zod(&type_ref.generics[1])
99 } else {
100 "z.unknown()".to_string()
101 };
102 format!("z.record({})", value_schema)
104 }
105 "HashSet" | "BTreeSet" => {
106 if !type_ref.generics.is_empty() {
107 format!("z.set({})", self.type_to_zod(&type_ref.generics[0]))
108 } else {
109 "z.set(z.unknown())".to_string()
110 }
111 }
112 _ => self.get_zod_primitive_type(name),
113 }
114 }
115 name => format!("{}Schema", name),
117 }
118 }
119
120 fn get_zod_primitive_type(&self, type_name: &str) -> String {
121 match type_name {
122 "i8" | "i16" | "i32" | "u8" | "u16" | "u32" | "f32" | "f64" => "z.number()".to_string(),
123 "i64" | "i128" | "u64" | "u128" | "isize" | "usize" => {
124 if self.config.use_bigint {
125 "z.bigint()".to_string()
126 } else {
127 "z.number()".to_string()
128 }
129 }
130 "String" | "str" | "char" => "z.string()".to_string(),
131 "bool" => "z.boolean()".to_string(),
132 _ => "z.unknown()".to_string(),
133 }
134 }
135}
136
137impl Default for ValidationGenerator {
138 fn default() -> Self {
139 Self::new(GeneratorConfig::default())
140 }
141}