Skip to main content

rohas_parser/
ast.rs

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/// Attribute (e.g., @id, @unique, @default)
154#[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        // Add duplicate
279        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}