orbiton/templates/
components.rs

1use liquid::model::{ArrayView, DisplayCow, ObjectView, ScalarCow, Value, ValueView};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::fmt::Display;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub enum PropType {
9    String,
10    Number,
11    Boolean,
12    Array(Box<PropType>),
13    Object(HashMap<String, PropType>),
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Component {
18    pub name: String,
19    pub template: String,
20    pub props: HashMap<String, PropType>,
21    pub description: String,
22}
23
24#[allow(dead_code)]
25impl Component {
26    pub fn new(
27        name: String,
28        template: String,
29        props: HashMap<String, PropType>,
30        description: String,
31    ) -> Self {
32        Self {
33            name,
34            template,
35            props,
36            description,
37        }
38    }
39
40    pub fn add_prop(&mut self, name: String, prop_type: PropType) {
41        self.props.insert(name, prop_type);
42    }
43
44    pub fn generate_code(&self) -> String {
45        // Basic implementation - can be enhanced
46        format!(
47            "Component {{ name: {}, props: [{}] }}",
48            self.name,
49            self.props.keys().cloned().collect::<Vec<_>>().join(", ")
50        )
51    }
52}
53
54impl Display for Component {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{} ({})", self.name, self.description)
57    }
58}
59
60impl ValueView for Component {
61    fn as_debug(&self) -> &dyn Debug {
62        self
63    }
64
65    fn render(&self) -> DisplayCow<'_> {
66        DisplayCow::Owned(Box::new(self.to_string()))
67    }
68
69    fn source(&self) -> DisplayCow<'_> {
70        DisplayCow::Owned(Box::new(self.to_string()))
71    }
72
73    fn type_name(&self) -> &'static str {
74        "component"
75    }
76
77    fn query_state(&self, state: liquid::model::State) -> bool {
78        match state {
79            liquid::model::State::Truthy => true,
80            liquid::model::State::DefaultValue => false,
81            liquid::model::State::Empty => false,
82            _ => false,
83        }
84    }
85
86    fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
87        liquid::model::KStringCow::from_string(self.to_string())
88    }
89
90    fn to_value(&self) -> Value {
91        Value::Nil
92    }
93
94    fn as_object(&self) -> Option<&dyn ObjectView> {
95        None
96    }
97
98    fn as_array(&self) -> Option<&dyn ArrayView> {
99        None
100    }
101
102    fn as_scalar(&self) -> Option<ScalarCow<'_>> {
103        Some(ScalarCow::new(self.name.as_str()))
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_simple_component() {
113        let mut component = Component::new(
114            "Button".to_string(),
115            "button".to_string(),
116            HashMap::new(),
117            "A simple button component".to_string(),
118        );
119        component.add_prop("disabled".to_string(), PropType::Boolean);
120        component.add_prop("label".to_string(), PropType::String);
121
122        let code = component.generate_code();
123        assert!(code.contains("disabled"));
124        assert!(code.contains("label"));
125    }
126
127    #[test]
128    fn test_complex_component() {
129        let mut user_type = HashMap::new();
130        user_type.insert("name".to_string(), PropType::String);
131        user_type.insert("age".to_string(), PropType::Number);
132
133        let mut component = Component::new(
134            "UserCard".to_string(),
135            "user-card".to_string(),
136            HashMap::new(),
137            "A user card component".to_string(),
138        );
139        component.add_prop(
140            "tags".to_string(),
141            PropType::Array(Box::new(PropType::String)),
142        );
143        component.add_prop("user".to_string(), PropType::Object(user_type));
144
145        let code = component.generate_code();
146        assert!(code.contains("tags"));
147        assert!(code.contains("user"));
148    }
149}