1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4pub struct Schema {
5 pub models: Vec<Model>,
6 pub apis: Vec<Api>,
7 pub events: Vec<Event>,
8 pub crons: Vec<Cron>,
9 pub inputs: Vec<Input>,
10 pub websockets: Vec<WebSocket>,
11}
12
13impl Schema {
14 pub fn new() -> Self {
15 Self {
16 models: Vec::new(),
17 apis: Vec::new(),
18 events: Vec::new(),
19 crons: Vec::new(),
20 inputs: Vec::new(),
21 websockets: Vec::new(),
22 }
23 }
24
25 pub fn validate(&self) -> crate::Result<()> {
26 let mut names = std::collections::HashSet::new();
27
28 for model in &self.models {
29 if !names.insert(&model.name) {
30 return Err(crate::ParseError::DuplicateDefinition(format!(
31 "Model '{}'",
32 model.name
33 )));
34 }
35 }
36
37 for api in &self.apis {
38 if !names.insert(&api.name) {
39 return Err(crate::ParseError::DuplicateDefinition(format!(
40 "API '{}'",
41 api.name
42 )));
43 }
44 }
45
46 for event in &self.events {
47 if !names.insert(&event.name) {
48 return Err(crate::ParseError::DuplicateDefinition(format!(
49 "Event '{}'",
50 event.name
51 )));
52 }
53 }
54
55 for websocket in &self.websockets {
56 if !names.insert(&websocket.name) {
57 return Err(crate::ParseError::DuplicateDefinition(format!(
58 "WebSocket '{}'",
59 websocket.name
60 )));
61 }
62 }
63
64 Ok(())
65 }
66}
67
68impl Default for Schema {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75pub struct Model {
76 pub name: String,
77 pub fields: Vec<Field>,
78 pub attributes: Vec<Attribute>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
82pub struct Field {
83 pub name: String,
84 pub field_type: FieldType,
85 pub optional: bool,
86 pub attributes: Vec<Attribute>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
90pub enum FieldType {
91 Int,
92 String,
93 Boolean,
94 Float,
95 DateTime,
96 Json,
97 Custom(String),
98 Array(Box<FieldType>),
99}
100
101impl FieldType {
102 pub fn from_str(s: &str) -> Self {
103 match s {
104 "Int" => FieldType::Int,
105 "String" => FieldType::String,
106 "Boolean" | "Bool" => FieldType::Boolean,
107 "Float" => FieldType::Float,
108 "DateTime" => FieldType::DateTime,
109 "Json" => FieldType::Json,
110 _ => FieldType::Custom(s.to_string()),
111 }
112 }
113
114 pub fn to_typescript(&self) -> String {
115 match self {
116 FieldType::Int | FieldType::Float => "number".to_string(),
117 FieldType::String => "string".to_string(),
118 FieldType::Boolean => "boolean".to_string(),
119 FieldType::DateTime => "Date".to_string(),
120 FieldType::Json => "any".to_string(),
121 FieldType::Custom(name) => name.clone(),
122 FieldType::Array(inner) => format!("{}[]", inner.to_typescript()),
123 }
124 }
125
126 pub fn to_python(&self) -> String {
127 match self {
128 FieldType::Int => "int".to_string(),
129 FieldType::Float => "float".to_string(),
130 FieldType::String => "str".to_string(),
131 FieldType::Boolean => "bool".to_string(),
132 FieldType::DateTime => "datetime".to_string(),
133 FieldType::Json => "dict".to_string(),
134 FieldType::Custom(name) => name.clone(),
135 FieldType::Array(inner) => format!("list[{}]", inner.to_python()),
136 }
137 }
138
139 pub fn to_rust(&self) -> String {
140 match self {
141 FieldType::Int => "i64".to_string(),
142 FieldType::Float => "f64".to_string(),
143 FieldType::String => "String".to_string(),
144 FieldType::Boolean => "bool".to_string(),
145 FieldType::DateTime => "chrono::DateTime<chrono::Utc>".to_string(),
146 FieldType::Json => "serde_json::Value".to_string(),
147 FieldType::Custom(name) => name.clone(),
148 FieldType::Array(inner) => format!("Vec<{}>", inner.to_rust()),
149 }
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
155pub struct Attribute {
156 pub name: String,
157 pub args: Vec<String>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161pub struct Api {
162 pub name: String,
163 pub method: HttpMethod,
164 pub path: String,
165 pub body: Option<String>,
166 pub response: String,
167 pub triggers: Vec<String>,
168 pub middlewares: Vec<String>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
172pub enum HttpMethod {
173 GET,
174 POST,
175 PUT,
176 PATCH,
177 DELETE,
178}
179
180impl HttpMethod {
181 pub fn from_str(s: &str) -> Option<Self> {
182 match s.to_uppercase().as_str() {
183 "GET" => Some(HttpMethod::GET),
184 "POST" => Some(HttpMethod::POST),
185 "PUT" => Some(HttpMethod::PUT),
186 "PATCH" => Some(HttpMethod::PATCH),
187 "DELETE" => Some(HttpMethod::DELETE),
188 _ => None,
189 }
190 }
191}
192
193impl std::fmt::Display for HttpMethod {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 match self {
196 HttpMethod::GET => write!(f, "GET"),
197 HttpMethod::POST => write!(f, "POST"),
198 HttpMethod::PUT => write!(f, "PUT"),
199 HttpMethod::PATCH => write!(f, "PATCH"),
200 HttpMethod::DELETE => write!(f, "DELETE"),
201 }
202 }
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
206pub struct Event {
207 pub name: String,
208 pub payload: String,
209 pub handlers: Vec<String>,
210 pub triggers: Vec<String>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub adapter_type: Option<String>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
216pub struct Cron {
217 pub name: String,
218 pub schedule: String,
219 pub triggers: Vec<String>,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub struct Input {
224 pub name: String,
225 pub fields: Vec<Field>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
229pub struct WebSocket {
230 pub name: String,
231 pub path: String,
232 pub message: Option<String>,
233 pub on_connect: Vec<String>,
234 pub on_message: Vec<String>,
235 pub on_disconnect: Vec<String>,
236 pub triggers: Vec<String>,
237 pub broadcast: bool,
238 pub middlewares: Vec<String>,
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_field_type_to_typescript() {
247 assert_eq!(FieldType::Int.to_typescript(), "number");
248 assert_eq!(FieldType::String.to_typescript(), "string");
249 assert_eq!(FieldType::Boolean.to_typescript(), "boolean");
250 assert_eq!(
251 FieldType::Array(Box::new(FieldType::String)).to_typescript(),
252 "string[]"
253 );
254 }
255
256 #[test]
257 fn test_field_type_to_python() {
258 assert_eq!(FieldType::Int.to_python(), "int");
259 assert_eq!(FieldType::String.to_python(), "str");
260 assert_eq!(FieldType::Boolean.to_python(), "bool");
261 assert_eq!(
262 FieldType::Array(Box::new(FieldType::Int)).to_python(),
263 "list[int]"
264 );
265 }
266
267 #[test]
268 fn test_schema_validation() {
269 let mut schema = Schema::new();
270 schema.models.push(Model {
271 name: "User".to_string(),
272 fields: vec![],
273 attributes: vec![],
274 });
275
276 assert!(schema.validate().is_ok());
277
278 schema.models.push(Model {
280 name: "User".to_string(),
281 fields: vec![],
282 attributes: vec![],
283 });
284
285 assert!(schema.validate().is_err());
286 }
287}