1use kotoba_core::prelude::*;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub enum ComponentType {
12 Server,
14 Client,
16 Layout,
18 Page,
20 Loading,
22 Error,
24 Template,
26 NotFound,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub enum ExecutionEnvironment {
33 ServerOnly,
35 ClientOnly,
37 Universal,
39}
40
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub struct ComponentIR {
44 pub id: String,
45 pub name: String,
46 pub component_type: ComponentType,
47 pub environment: ExecutionEnvironment,
48 pub props: Properties,
49 pub state: Properties,
50 pub children: Vec<ComponentIR>,
51 pub imports: Vec<ImportIR>,
52 pub source_hash: ContentHash,
53 pub metadata: Properties,
54}
55
56impl ComponentIR {
57 pub fn new(name: String, component_type: ComponentType) -> Self {
58 let environment = match component_type {
59 ComponentType::Server => ExecutionEnvironment::ServerOnly,
60 ComponentType::Client => ExecutionEnvironment::ClientOnly,
61 _ => ExecutionEnvironment::Universal,
62 };
63
64 Self {
65 id: format!("component_{}", uuid::Uuid::new_v4()),
66 name,
67 component_type,
68 environment,
69 props: Properties::new(),
70 state: Properties::new(),
71 children: Vec::new(),
72 imports: Vec::new(),
73 source_hash: ContentHash::sha256([0; 32]), metadata: Properties::new(),
75 }
76 }
77
78 pub fn add_child(&mut self, child: ComponentIR) {
80 self.children.push(child);
81 }
82
83 pub fn set_prop(&mut self, key: String, value: Value) {
85 self.props.insert(key, value);
86 }
87
88 pub fn set_state(&mut self, key: String, value: Value) {
90 self.state.insert(key, value);
91 }
92
93 pub fn add_import(&mut self, import: ImportIR) {
95 self.imports.push(import);
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub struct ImportIR {
102 pub module: String,
103 pub specifiers: Vec<String>,
104 pub is_default: bool,
105 pub alias: Option<String>,
106}
107
108impl ImportIR {
109 pub fn new(module: String) -> Self {
110 Self {
111 module,
112 specifiers: Vec::new(),
113 is_default: false,
114 alias: None,
115 }
116 }
117
118 pub fn add_specifier(&mut self, specifier: String) {
119 self.specifiers.push(specifier);
120 }
121
122 pub fn set_default(&mut self, is_default: bool) {
123 self.is_default = is_default;
124 }
125
126 pub fn set_alias(&mut self, alias: String) {
127 self.alias = Some(alias);
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133pub struct ElementIR {
134 pub tag_name: String,
135 pub attributes: Properties,
136 pub children: Vec<ElementChild>,
137 pub key: Option<String>,
138}
139
140#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
141pub enum ElementChild {
142 Element(ElementIR),
143 Text(String),
144 Component(ComponentIR),
145 Expression(String), }
147
148impl ElementIR {
149 pub fn new(tag_name: String) -> Self {
150 Self {
151 tag_name,
152 attributes: Properties::new(),
153 children: Vec::new(),
154 key: None,
155 }
156 }
157
158 pub fn add_attribute(&mut self, key: String, value: Value) {
159 self.attributes.insert(key, value);
160 }
161
162 pub fn add_child(&mut self, child: ElementChild) {
163 self.children.push(child);
164 }
165
166 pub fn set_key(&mut self, key: String) {
167 self.key = Some(key);
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub enum HookIR {
174 UseState {
175 variable: String,
176 initial_value: Value,
177 },
178 UseEffect {
179 dependencies: Vec<String>,
180 effect_code: String,
181 },
182 UseContext {
183 context_name: String,
184 variable: String,
185 },
186 UseReducer {
187 reducer_name: String,
188 initial_state: Value,
189 variable: String,
190 },
191 CustomHook {
192 name: String,
193 args: Vec<Value>,
194 return_variable: String,
195 },
196}
197
198#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub struct EventHandlerIR {
201 pub event_type: String, pub handler_function: String, pub prevent_default: bool,
204 pub stop_propagation: bool,
205}
206
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209pub struct ComponentMetadata {
210 pub is_async: bool,
211 pub uses_suspense: bool,
212 pub has_error_boundary: bool,
213 pub cache_strategy: Option<CacheStrategy>,
214 pub revalidation_strategy: Option<RevalidationStrategy>,
215}
216
217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218pub enum CacheStrategy {
219 NoCache,
220 Cache { duration: u64 },
221 Revalidate { tags: Vec<String> },
222}
223
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub enum RevalidationStrategy {
226 Never,
227 OnDemand,
228 TimeBased { interval: u64 },
229 TagBased { tags: Vec<String> },
230}
231
232#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
234pub struct JSXIR {
235 pub elements: Vec<ElementIR>,
236 pub components: Vec<ComponentIR>,
237 pub hooks: Vec<HookIR>,
238 pub event_handlers: Vec<EventHandlerIR>,
239 pub metadata: ComponentMetadata,
240}
241
242impl JSXIR {
243 pub fn new() -> Self {
244 Self {
245 elements: Vec::new(),
246 components: Vec::new(),
247 hooks: Vec::new(),
248 event_handlers: Vec::new(),
249 metadata: ComponentMetadata {
250 is_async: false,
251 uses_suspense: false,
252 has_error_boundary: false,
253 cache_strategy: None,
254 revalidation_strategy: None,
255 },
256 }
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262pub struct ComponentTreeIR {
263 pub root: ComponentIR,
264 pub route_context: Properties, pub global_state: Properties, }
267
268impl ComponentTreeIR {
269 pub fn new(root: ComponentIR) -> Self {
270 Self {
271 root,
272 route_context: Properties::new(),
273 global_state: Properties::new(),
274 }
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_component_ir_creation() {
284 let mut component = ComponentIR::new("MyComponent".to_string(), ComponentType::Server);
285 component.set_prop("title".to_string(), Value::String("Hello".to_string()));
286
287 assert_eq!(component.name, "MyComponent");
288 assert_eq!(component.component_type, ComponentType::Server);
289 assert_eq!(component.environment, ExecutionEnvironment::ServerOnly);
290 }
291
292 #[test]
293 fn test_element_ir_creation() {
294 let mut element = ElementIR::new("div".to_string());
295 element.add_attribute("className".to_string(), Value::String("container".to_string()));
296 element.add_child(ElementChild::Text("Hello World".to_string()));
297
298 assert_eq!(element.tag_name, "div");
299 assert_eq!(element.children.len(), 1);
300 }
301
302 #[test]
303 fn test_import_ir_creation() {
304 let mut import = ImportIR::new("react".to_string());
305 import.add_specifier("useState".to_string());
306 import.add_specifier("useEffect".to_string());
307
308 assert_eq!(import.module, "react");
309 assert_eq!(import.specifiers.len(), 2);
310 }
311}