kotoba_server/frontend/
component_ir.rs

1//! コンポーネントIR定義
2//!
3//! ReactコンポーネントをKotobaのIRで表現します。
4
5use kotoba_core::prelude::*;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// コンポーネントの種類
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub enum ComponentType {
12    /// Server Component (デフォルト)
13    Server,
14    /// Client Component ('use client' ディレクティブ付き)
15    Client,
16    /// Layout Component (layout.js)
17    Layout,
18    /// Page Component (page.js)
19    Page,
20    /// Loading Component (loading.js)
21    Loading,
22    /// Error Component (error.js)
23    Error,
24    /// Template Component (template.js)
25    Template,
26    /// Not Found Component (not-found.js)
27    NotFound,
28}
29
30/// コンポーネントの実行環境
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub enum ExecutionEnvironment {
33    /// サーバーサイドでのみ実行
34    ServerOnly,
35    /// クライアントサイドでのみ実行
36    ClientOnly,
37    /// サーバー/クライアント両方で実行可能
38    Universal,
39}
40
41/// コンポーネントIR
42#[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]), // TODO: 実際のソースから計算
74            metadata: Properties::new(),
75        }
76    }
77
78    /// 子コンポーネントを追加
79    pub fn add_child(&mut self, child: ComponentIR) {
80        self.children.push(child);
81    }
82
83    /// Propsを設定
84    pub fn set_prop(&mut self, key: String, value: Value) {
85        self.props.insert(key, value);
86    }
87
88    /// Stateを設定
89    pub fn set_state(&mut self, key: String, value: Value) {
90        self.state.insert(key, value);
91    }
92
93    /// Importを追加
94    pub fn add_import(&mut self, import: ImportIR) {
95        self.imports.push(import);
96    }
97}
98
99/// Import IR
100#[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/// 仮想DOM要素IR
132#[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), // JSX式 {variable}
146}
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/// フックIR (React Hooksの表現)
172#[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/// イベントハンドラーIR
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub struct EventHandlerIR {
201    pub event_type: String, // 'onClick', 'onChange', etc.
202    pub handler_function: String, // 関数名
203    pub prevent_default: bool,
204    pub stop_propagation: bool,
205}
206
207/// コンポーネントメタデータ
208#[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/// JSX/TSXパーサー結果のIR表現
233#[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/// コンポーネントツリーIR
261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262pub struct ComponentTreeIR {
263    pub root: ComponentIR,
264    pub route_context: Properties, // ルーティングコンテキスト
265    pub global_state: Properties, // グローバル状態
266}
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}