1use nautilus_schema::ir::{
9 DefaultValue, EnumIr, FieldIr, FunctionCall, ResolvedFieldType, ScalarType,
10};
11use serde::Serialize;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize)]
21pub struct FilterOperator {
22 pub suffix: String,
23 pub type_name: String,
24}
25
26pub trait LanguageBackend {
38 fn scalar_to_type(&self, scalar: &ScalarType) -> &'static str;
40
41 fn array_type(&self, inner: &str) -> String;
45
46 fn not_in_suffix(&self) -> &'static str;
50
51 fn startswith_suffix(&self) -> &'static str;
55
56 fn endswith_suffix(&self) -> &'static str;
60
61 fn null_suffix(&self) -> &'static str;
65
66 fn is_auto_generated(&self, field: &FieldIr) -> bool {
73 if field.computed.is_some() {
74 return true;
75 }
76 if let Some(default) = &field.default_value {
77 matches!(
78 default,
79 DefaultValue::Function(f)
80 if f.name == "autoincrement" || f.name == "uuid" || f.name == "now"
81 )
82 } else {
83 false
84 }
85 }
86
87 fn numeric_operators(&self, type_name: &str) -> Vec<FilterOperator> {
90 let arr = self.array_type(type_name);
91 vec![
92 FilterOperator {
93 suffix: "lt".to_string(),
94 type_name: type_name.to_string(),
95 },
96 FilterOperator {
97 suffix: "lte".to_string(),
98 type_name: type_name.to_string(),
99 },
100 FilterOperator {
101 suffix: "gt".to_string(),
102 type_name: type_name.to_string(),
103 },
104 FilterOperator {
105 suffix: "gte".to_string(),
106 type_name: type_name.to_string(),
107 },
108 FilterOperator {
109 suffix: "in".to_string(),
110 type_name: arr.clone(),
111 },
112 FilterOperator {
113 suffix: self.not_in_suffix().to_string(),
114 type_name: arr,
115 },
116 ]
117 }
118
119 fn get_filter_operators_for_scalar(&self, scalar: &ScalarType) -> Vec<FilterOperator> {
121 let mut ops: Vec<FilterOperator> = Vec::new();
122
123 match scalar {
124 ScalarType::String => {
125 let str_t = self.scalar_to_type(&ScalarType::String);
126 let arr = self.array_type(str_t);
127 ops.push(FilterOperator {
128 suffix: "contains".to_string(),
129 type_name: str_t.to_string(),
130 });
131 ops.push(FilterOperator {
132 suffix: self.startswith_suffix().to_string(),
133 type_name: str_t.to_string(),
134 });
135 ops.push(FilterOperator {
136 suffix: self.endswith_suffix().to_string(),
137 type_name: str_t.to_string(),
138 });
139 ops.push(FilterOperator {
140 suffix: "in".to_string(),
141 type_name: arr.clone(),
142 });
143 ops.push(FilterOperator {
144 suffix: self.not_in_suffix().to_string(),
145 type_name: arr,
146 });
147 }
148 ScalarType::Int | ScalarType::BigInt => {
149 ops.extend(self.numeric_operators(self.scalar_to_type(scalar)));
150 }
151 ScalarType::Float => {
152 ops.extend(self.numeric_operators(self.scalar_to_type(scalar)));
153 }
154 ScalarType::Decimal { .. } => {
155 ops.extend(self.numeric_operators(self.scalar_to_type(scalar)));
156 }
157 ScalarType::DateTime => {
158 ops.extend(self.numeric_operators(self.scalar_to_type(scalar)));
159 }
160 ScalarType::Uuid => {
161 let uuid_t = self.scalar_to_type(&ScalarType::Uuid);
162 let arr = self.array_type(uuid_t);
163 ops.push(FilterOperator {
164 suffix: "in".to_string(),
165 type_name: arr.clone(),
166 });
167 ops.push(FilterOperator {
168 suffix: self.not_in_suffix().to_string(),
169 type_name: arr,
170 });
171 }
172 ScalarType::Xml | ScalarType::Char { .. } | ScalarType::VarChar { .. } => {
173 let str_t = self.scalar_to_type(scalar);
174 let arr = self.array_type(str_t);
175 ops.push(FilterOperator {
176 suffix: "contains".to_string(),
177 type_name: str_t.to_string(),
178 });
179 ops.push(FilterOperator {
180 suffix: self.startswith_suffix().to_string(),
181 type_name: str_t.to_string(),
182 });
183 ops.push(FilterOperator {
184 suffix: self.endswith_suffix().to_string(),
185 type_name: str_t.to_string(),
186 });
187 ops.push(FilterOperator {
188 suffix: "in".to_string(),
189 type_name: arr.clone(),
190 });
191 ops.push(FilterOperator {
192 suffix: self.not_in_suffix().to_string(),
193 type_name: arr,
194 });
195 }
196 ScalarType::Boolean | ScalarType::Bytes | ScalarType::Json | ScalarType::Jsonb => {}
198 }
199
200 ops.push(FilterOperator {
202 suffix: "not".to_string(),
203 type_name: self.scalar_to_type(scalar).to_string(),
204 });
205
206 ops
207 }
208
209 fn get_filter_operators_for_field(
212 &self,
213 field: &FieldIr,
214 enums: &HashMap<String, EnumIr>,
215 ) -> Vec<FilterOperator> {
216 let mut ops: Vec<FilterOperator> = Vec::new();
217
218 match &field.field_type {
219 ResolvedFieldType::Scalar(scalar) => {
220 ops = self.get_filter_operators_for_scalar(scalar);
221 }
222 ResolvedFieldType::Enum { enum_name } => {
223 let enum_type = if enums.contains_key(enum_name) {
224 enum_name.clone()
225 } else {
226 self.scalar_to_type(&ScalarType::String).to_string()
228 };
229 let arr = self.array_type(&enum_type);
230 ops.push(FilterOperator {
231 suffix: "in".to_string(),
232 type_name: arr.clone(),
233 });
234 ops.push(FilterOperator {
235 suffix: self.not_in_suffix().to_string(),
236 type_name: arr,
237 });
238 ops.push(FilterOperator {
239 suffix: "not".to_string(),
240 type_name: enum_type,
241 });
242 }
243 ResolvedFieldType::Relation(_) | ResolvedFieldType::CompositeType { .. } => {
244 }
246 }
247
248 if !field.is_required || self.is_auto_generated(field) {
250 ops.push(FilterOperator {
251 suffix: self.null_suffix().to_string(),
252 type_name: self.scalar_to_type(&ScalarType::Boolean).to_string(),
253 });
254 }
255
256 ops
257 }
258
259 fn null_literal(&self) -> &'static str;
261
262 fn true_literal(&self) -> &'static str;
264
265 fn false_literal(&self) -> &'static str;
267
268 fn string_literal(&self, s: &str) -> String;
270
271 fn empty_array_literal(&self) -> &'static str;
273
274 fn enum_variant_literal(&self, variant: &str) -> String {
276 variant.to_string()
277 }
278
279 fn relation_type(&self, target_model: &str) -> String {
283 target_model.to_string()
284 }
285
286 fn get_base_type(&self, field: &FieldIr, enums: &HashMap<String, EnumIr>) -> String {
288 match &field.field_type {
289 ResolvedFieldType::Scalar(scalar) => self.scalar_to_type(scalar).to_string(),
290 ResolvedFieldType::Enum { enum_name } => {
291 if enums.contains_key(enum_name) {
292 enum_name.clone()
293 } else {
294 self.scalar_to_type(&ScalarType::String).to_string()
295 }
296 }
297 ResolvedFieldType::CompositeType { type_name } => type_name.clone(),
298 ResolvedFieldType::Relation(rel) => self.relation_type(&rel.target_model),
299 }
300 }
301
302 fn get_default_value(&self, field: &FieldIr) -> Option<String> {
304 if let Some(default) = &field.default_value {
305 match default {
306 DefaultValue::Function(FunctionCall { name, .. })
307 if matches!(name.as_str(), "now" | "uuid" | "autoincrement") =>
308 {
309 return Some(self.null_literal().to_string());
310 }
311 DefaultValue::Function(_) => return None,
312 DefaultValue::String(s) => return Some(self.string_literal(s)),
313 DefaultValue::Number(n) => return Some(n.clone()),
314 DefaultValue::Boolean(b) => {
315 return Some(if *b {
316 self.true_literal().to_string()
317 } else {
318 self.false_literal().to_string()
319 });
320 }
321 DefaultValue::EnumVariant(v) => return Some(self.enum_variant_literal(v)),
322 }
323 }
324
325 if field.is_array {
326 Some(self.empty_array_literal().to_string())
327 } else if !field.is_required {
328 Some(self.null_literal().to_string())
329 } else {
330 None
331 }
332 }
333}