1use kotoba_core::prelude::*;
6use crate::frontend::component_ir::ComponentIR;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum ApiMethod {
13 GET,
14 POST,
15 PUT,
16 DELETE,
17 PATCH,
18 HEAD,
19 OPTIONS,
20 TRACE,
21 CONNECT,
22}
23
24impl std::fmt::Display for ApiMethod {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 ApiMethod::GET => write!(f, "GET"),
28 ApiMethod::POST => write!(f, "POST"),
29 ApiMethod::PUT => write!(f, "PUT"),
30 ApiMethod::DELETE => write!(f, "DELETE"),
31 ApiMethod::PATCH => write!(f, "PATCH"),
32 ApiMethod::HEAD => write!(f, "HEAD"),
33 ApiMethod::OPTIONS => write!(f, "OPTIONS"),
34 ApiMethod::TRACE => write!(f, "TRACE"),
35 ApiMethod::CONNECT => write!(f, "CONNECT"),
36 }
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub enum ResponseFormat {
43 JSON,
44 XML,
45 HTML,
46 Text,
47 Binary,
48 GraphQL,
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct ApiRouteIR {
54 pub path: String,
55 pub method: ApiMethod,
56 pub handler: ApiHandlerIR,
57 pub middlewares: Vec<String>, pub response_format: ResponseFormat,
59 pub parameters: ApiParameters,
60 pub metadata: ApiMetadata,
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64pub struct ApiHandlerIR {
65 pub function_name: String,
66 pub component: Option<ComponentIR>, pub is_async: bool,
68 pub timeout_ms: Option<u64>,
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub struct ApiParameters {
73 pub path_params: Vec<ApiParameter>,
74 pub query_params: Vec<ApiParameter>,
75 pub body_params: Option<ApiBodySchema>,
76 pub headers: Vec<ApiParameter>,
77}
78
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct ApiParameter {
81 pub name: String,
82 pub param_type: ParameterType,
83 pub required: bool,
84 pub default_value: Option<Value>,
85 pub validation: Option<ValidationRules>,
86}
87
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub enum ParameterType {
90 String,
91 Integer,
92 Float,
93 Boolean,
94 Array,
95 Object,
96 File,
97 Date,
98 DateTime,
99}
100
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub struct ValidationRules {
103 pub min_length: Option<usize>,
104 pub max_length: Option<usize>,
105 pub pattern: Option<String>,
106 pub min_value: Option<f64>,
107 pub max_value: Option<f64>,
108 pub allowed_values: Vec<Value>,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct ApiBodySchema {
113 pub content_type: String,
114 pub schema: Value, }
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub struct ApiMetadata {
119 pub description: Option<String>,
120 pub summary: Option<String>,
121 pub tags: Vec<String>,
122 pub deprecated: bool,
123 pub rate_limit: Option<RateLimit>,
124 pub cache: Option<CacheConfig>,
125}
126
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128pub struct RateLimit {
129 pub requests: u32,
130 pub window_seconds: u64,
131 pub strategy: RateLimitStrategy,
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub enum RateLimitStrategy {
136 FixedWindow,
137 SlidingWindow,
138 TokenBucket,
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142pub struct CacheConfig {
143 pub ttl_seconds: u64,
144 pub vary_by: Vec<String>, }
146
147#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
149pub struct ApiResponseIR {
150 pub status_code: u16,
151 pub headers: Properties,
152 pub body: ApiResponseBody,
153 pub metadata: ResponseMetadata,
154}
155
156#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
157pub enum ApiResponseBody {
158 JSON(Value),
159 Text(String),
160 HTML(String),
161 Binary(Vec<u8>),
162 File { path: String, filename: String },
163 }
165
166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
167pub struct ResponseMetadata {
168 pub content_type: String,
169 pub content_length: Option<usize>,
170 pub cache_control: Option<String>,
171 pub etag: Option<String>,
172}
173
174#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176pub struct DatabaseIR {
177 pub connection_string: String,
178 pub db_type: DatabaseType,
179 pub models: Vec<ModelIR>,
180 pub migrations: Vec<MigrationIR>,
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
184pub enum DatabaseType {
185 PostgreSQL,
186 MySQL,
187 SQLite,
188 MongoDB,
189 Redis,
190 Custom(String),
191}
192
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct ModelIR {
195 pub name: String,
196 pub table_name: String,
197 pub fields: Vec<FieldIR>,
198 pub relationships: Vec<RelationshipIR>,
199 pub indexes: Vec<IndexIR>,
200}
201
202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
203pub struct FieldIR {
204 pub name: String,
205 pub field_type: FieldType,
206 pub nullable: bool,
207 pub default_value: Option<Value>,
208 pub unique: bool,
209 pub primary_key: bool,
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
213pub enum FieldType {
214 String { max_length: Option<usize> },
215 Text,
216 Integer,
217 BigInt,
218 Float,
219 Double,
220 Decimal { precision: u32, scale: u32 },
221 Boolean,
222 Date,
223 DateTime,
224 Time,
225 UUID,
226 JSON,
227 Binary,
228}
229
230#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231pub struct RelationshipIR {
232 pub name: String,
233 pub target_model: String,
234 pub relationship_type: RelationshipType,
235 pub foreign_key: String,
236 pub on_delete: CascadeAction,
237 pub on_update: CascadeAction,
238}
239
240#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
241pub enum RelationshipType {
242 OneToOne,
243 OneToMany,
244 ManyToMany,
245}
246
247#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
248pub enum CascadeAction {
249 Cascade,
250 Restrict,
251 SetNull,
252 SetDefault,
253 NoAction,
254}
255
256#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
257pub struct IndexIR {
258 pub name: String,
259 pub fields: Vec<String>,
260 pub unique: bool,
261 pub index_type: IndexType,
262}
263
264#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265pub enum IndexType {
266 BTree,
267 Hash,
268 GIN,
269 GiST,
270 SPGiST,
271 BRIN,
272}
273
274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
275pub struct MigrationIR {
276 pub version: String,
277 pub description: String,
278 pub up_sql: String,
279 pub down_sql: String,
280 pub dependencies: Vec<String>,
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
285pub struct MiddlewareIR {
286 pub name: String,
287 pub middleware_type: MiddlewareType,
288 pub config: Properties,
289 pub order: i32,
290}
291
292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
293pub enum MiddlewareType {
294 Authentication,
295 Authorization,
296 CORS,
297 Compression,
298 CSRF,
299 Logging,
300 RateLimiting,
301 Session,
302 StaticFiles,
303 Custom(String),
304}
305
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
308pub struct WebSocketIR {
309 pub path: String,
310 pub handler: WebSocketHandlerIR,
311 pub protocols: Vec<String>,
312 pub heartbeat_interval: Option<u64>,
313}
314
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
316pub struct WebSocketHandlerIR {
317 pub on_connect: String,
318 pub on_message: String,
319 pub on_disconnect: String,
320 pub on_error: String,
321}
322
323#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
325pub struct GraphQLIR {
326 pub schema: GraphQLSchemaIR,
327 pub resolvers: HashMap<String, GraphQLResolverIR>,
328 pub directives: Vec<GraphQLDirectiveIR>,
329}
330
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub struct GraphQLSchemaIR {
333 pub query: Option<String>,
334 pub mutation: Option<String>,
335 pub subscription: Option<String>,
336 pub types: Vec<GraphQLTypeIR>,
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
340pub struct GraphQLTypeIR {
341 pub name: String,
342 pub kind: GraphQLTypeKind,
343 pub fields: Vec<GraphQLFieldIR>,
344}
345
346#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
347pub enum GraphQLTypeKind {
348 Object,
349 Interface,
350 Union,
351 Enum,
352 InputObject,
353 Scalar,
354}
355
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357pub struct GraphQLFieldIR {
358 pub name: String,
359 pub field_type: String,
360 pub args: Vec<GraphQLArgumentIR>,
361 pub resolver: Option<String>,
362}
363
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
365pub struct GraphQLArgumentIR {
366 pub name: String,
367 pub arg_type: String,
368 pub default_value: Option<Value>,
369}
370
371#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
372pub struct GraphQLResolverIR {
373 pub field_name: String,
374 pub function_name: String,
375 pub is_async: bool,
376}
377
378#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
379pub struct GraphQLDirectiveIR {
380 pub name: String,
381 pub locations: Vec<DirectiveLocation>,
382 pub args: Vec<GraphQLArgumentIR>,
383}
384
385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
386pub enum DirectiveLocation {
387 Query,
388 Mutation,
389 Subscription,
390 Field,
391 FragmentDefinition,
392 FragmentSpread,
393 InlineFragment,
394 Schema,
395 Scalar,
396 Object,
397 FieldDefinition,
398 ArgumentDefinition,
399 Interface,
400 Union,
401 Enum,
402 EnumValue,
403 InputObject,
404 InputFieldDefinition,
405}
406
407#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
409pub struct WebFrameworkConfigIR {
410 pub server: ServerConfig,
411 pub database: Option<DatabaseIR>,
412 pub api_routes: Vec<ApiRouteIR>,
413 pub web_sockets: Vec<WebSocketIR>,
414 pub graph_ql: Option<GraphQLIR>,
415 pub middlewares: Vec<MiddlewareIR>,
416 pub static_files: Vec<StaticFilesConfig>,
417 pub authentication: Option<AuthConfig>,
418 pub session: Option<SessionConfig>,
419}
420
421#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
422pub struct ServerConfig {
423 pub host: String,
424 pub port: u16,
425 pub tls: Option<TLSConfig>,
426 pub workers: usize,
427 pub max_connections: usize,
428}
429
430#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
431pub struct TLSConfig {
432 pub cert_path: String,
433 pub key_path: String,
434 pub ca_path: Option<String>,
435}
436
437#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
438pub struct StaticFilesConfig {
439 pub route: String,
440 pub directory: String,
441 pub cache_control: Option<String>,
442 pub gzip: bool,
443}
444
445#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
446pub struct AuthConfig {
447 pub provider: AuthProvider,
448 pub config: Properties,
449 pub jwt_secret: Option<String>,
450 pub session_timeout: u64,
451}
452
453#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
454pub enum AuthProvider {
455 Local,
456 OAuth2,
457 LDAP,
458 SAML,
459 Custom(String),
460}
461
462#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
463pub struct SessionConfig {
464 pub store: SessionStore,
465 pub cookie_name: String,
466 pub secure: bool,
467 pub http_only: bool,
468 pub same_site: SameSitePolicy,
469}
470
471#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
472pub enum SessionStore {
473 Memory,
474 Redis,
475 Database,
476 File,
477}
478
479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480pub enum SameSitePolicy {
481 Strict,
482 Lax,
483 None,
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn test_api_route_creation() {
492 let route = ApiRouteIR {
493 path: "/api/users".to_string(),
494 method: ApiMethod::GET,
495 handler: ApiHandlerIR {
496 function_name: "getUsers".to_string(),
497 component: None,
498 is_async: true,
499 timeout_ms: Some(5000),
500 },
501 middlewares: vec!["auth".to_string(), "cors".to_string()],
502 response_format: ResponseFormat::JSON,
503 parameters: ApiParameters {
504 path_params: Vec::new(),
505 query_params: vec![ApiParameter {
506 name: "limit".to_string(),
507 param_type: ParameterType::Integer,
508 required: false,
509 default_value: Some(Value::Int(10)),
510 validation: Some(ValidationRules {
511 min_length: None,
512 max_length: None,
513 pattern: None,
514 min_value: Some(1.0),
515 max_value: Some(100.0),
516 allowed_values: Vec::new(),
517 }),
518 }],
519 body_params: None,
520 headers: Vec::new(),
521 },
522 metadata: ApiMetadata {
523 description: Some("Get users list".to_string()),
524 summary: Some("Users API".to_string()),
525 tags: vec!["users".to_string()],
526 deprecated: false,
527 rate_limit: Some(RateLimit {
528 requests: 100,
529 window_seconds: 60,
530 strategy: RateLimitStrategy::SlidingWindow,
531 }),
532 cache: Some(CacheConfig {
533 ttl_seconds: 300,
534 vary_by: vec!["user_id".to_string()],
535 }),
536 },
537 };
538
539 assert_eq!(route.path, "/api/users");
540 assert_eq!(route.method, ApiMethod::GET);
541 assert_eq!(route.handler.function_name, "getUsers");
542 assert!(route.handler.is_async);
543 }
544
545 #[test]
546 fn test_database_model_creation() {
547 let model = ModelIR {
548 name: "User".to_string(),
549 table_name: "users".to_string(),
550 fields: vec![
551 FieldIR {
552 name: "id".to_string(),
553 field_type: FieldType::UUID,
554 nullable: false,
555 default_value: None,
556 unique: true,
557 primary_key: true,
558 },
559 FieldIR {
560 name: "email".to_string(),
561 field_type: FieldType::String { max_length: Some(255) },
562 nullable: false,
563 default_value: None,
564 unique: true,
565 primary_key: false,
566 },
567 FieldIR {
568 name: "created_at".to_string(),
569 field_type: FieldType::DateTime,
570 nullable: false,
571 default_value: Some(Value::String("NOW()".to_string())),
572 unique: false,
573 primary_key: false,
574 },
575 ],
576 relationships: vec![
577 RelationshipIR {
578 name: "posts".to_string(),
579 target_model: "Post".to_string(),
580 relationship_type: RelationshipType::OneToMany,
581 foreign_key: "user_id".to_string(),
582 on_delete: CascadeAction::Cascade,
583 on_update: CascadeAction::NoAction,
584 },
585 ],
586 indexes: vec![
587 IndexIR {
588 name: "idx_users_email".to_string(),
589 fields: vec!["email".to_string()],
590 unique: true,
591 index_type: IndexType::BTree,
592 },
593 ],
594 };
595
596 assert_eq!(model.name, "User");
597 assert_eq!(model.table_name, "users");
598 assert_eq!(model.fields.len(), 3);
599 assert_eq!(model.relationships.len(), 1);
600 assert_eq!(model.indexes.len(), 1);
601 }
602
603 #[test]
604 fn test_middleware_creation() {
605 let middleware = MiddlewareIR {
606 name: "cors".to_string(),
607 middleware_type: MiddlewareType::CORS,
608 config: {
609 let mut props = Properties::new();
610 props.insert("allowed_origins".to_string(), Value::String("*".to_string()));
611 props.insert("allowed_methods".to_string(), Value::String("GET,POST,PUT,DELETE".to_string()));
612 props
613 },
614 order: 1,
615 };
616
617 assert_eq!(middleware.name, "cors");
618 assert_eq!(middleware.middleware_type, MiddlewareType::CORS);
619 assert_eq!(middleware.order, 1);
620 }
621}