1#[path = "iris/mod.rs"]
33pub mod iris;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49use std::fs;
50
51#[derive(Debug, Clone, Copy)]
53pub struct StyleDecl<'a> {
54 pub name: &'a str,
55 pub value: &'a str,
56}
57
58#[derive(Debug, Clone, Copy)]
60pub struct Attribute<'a> {
61 pub name: &'a str,
62 pub value: &'a str,
63}
64
65#[derive(Debug)]
67pub enum StyleProp<'a> {
68 Background(&'a str),
69 Color(&'a str),
70 Width(&'a str),
71 Height(&'a str),
72 FontSize(&'a str),
73 Raw(&'a str, &'a str),
74}
75
76pub fn parse_tag<'a>(input: &'a str) -> Option<(&'a str, &'a str)> {
87 let input = input.trim();
88 if !input.starts_with('<') || !input.ends_with('>') {
89 return None;
90 }
91 let inner = &input[1..input.len() - 1];
92 let mut parts = inner.splitn(2, char::is_whitespace);
93 let tag_name = parts.next()?.trim();
94 let attrs = parts.next().unwrap_or("").trim();
95 Some((tag_name, attrs))
96}
97
98pub fn parse_attributes<'a>(input: &'a str, out: &mut [Attribute<'a>]) -> usize {
112 let mut count = 0;
113 let mut i = 0;
114 let bytes = input.as_bytes();
115
116 while i < bytes.len() && count < out.len() {
117 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
118 i += 1;
119 }
120 if i >= bytes.len() {
121 break;
122 }
123
124 let start = i;
125 while i < bytes.len() && bytes[i] != b'=' && !bytes[i].is_ascii_whitespace() {
126 i += 1;
127 }
128 let name = &input[start..i].trim();
129
130 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
131 i += 1;
132 }
133 if i >= bytes.len() || bytes[i] != b'=' {
134 break;
135 }
136 i += 1;
137
138 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
139 i += 1;
140 }
141 if i >= bytes.len() || bytes[i] != b'"' {
142 break;
143 }
144 i += 1;
145 let value_start = i;
146
147 while i < bytes.len() && bytes[i] != b'"' {
148 i += 1;
149 }
150 if i > bytes.len() {
151 break;
152 }
153 let value = &input[value_start..i];
154 i += 1;
155
156 out[count] = Attribute { name, value };
157 count += 1;
158 }
159
160 count
161}
162
163pub fn parse_style<'a>(input: &'a str, out: &mut [StyleDecl<'a>]) -> usize {
177 let mut count = 0;
178 let mut rest = input;
179
180 while !rest.is_empty() && count < out.len() {
181 let semicolon = rest.find(';').unwrap_or(rest.len());
182 let decl = rest[..semicolon].trim();
183
184 if !decl.is_empty() {
185 if let Some((name, value)) = decl.split_once(':') {
186 let name = name.trim();
187 let value = value.trim();
188 if !name.is_empty() && !value.is_empty() {
189 out[count] = StyleDecl { name, value };
190 count += 1;
191 }
192 }
193 }
194
195 rest = if semicolon < rest.len() {
196 &rest[semicolon + 1..]
197 } else {
198 ""
199 };
200 }
201
202 count
203}
204
205pub fn normalize_decl<'a>(decl: StyleDecl<'a>) -> StyleProp<'a> {
221 match decl.name {
222 "b" => StyleProp::Background(decl.value),
223 "c" => StyleProp::Color(decl.value),
224 "w" => StyleProp::Width(decl.value),
225 "h" => StyleProp::Height(decl.value),
226 "f-sz" => StyleProp::FontSize(decl.value),
227 _ => StyleProp::Raw(decl.name, decl.value),
228 }
229}
230
231pub fn parse_neutron(input: &str) {
233 let mut attrs = [Attribute { name: "", value: "" }; 12];
234 let mut styles = [StyleDecl { name: "", value: "" }; 16];
235
236 let mut i = 0;
237 while let Some(start) = input[i..].find('<') {
238 let start = i + start;
239 if let Some(end) = input[start..].find('>') {
240 let end = start + end + 1;
241 let tag_content = &input[start..end];
242
243 if let Some((tag_name, attrs_text)) = parse_tag(tag_content) {
244 println!("[Tag] {}", tag_name);
245
246 let attr_count = parse_attributes(attrs_text, &mut attrs);
247 for attr in &attrs[..attr_count] {
248 println!(" [Attr] {} = \"{}\"", attr.name, attr.value);
249
250 if attr.name == "style" {
251 let style_count = parse_style(attr.value, &mut styles);
252 for style in &styles[..style_count] {
253 let prop = normalize_decl(*style);
254 println!(" [Style] {:?}", prop);
255 }
256 }
257 }
258 }
259
260 i = end;
261 } else {
262 break;
263 }
264 }
265}
266
267pub fn parse_neutron_file(path: &str) {
269 let neutron_code = match fs::read_to_string(path) {
270 Ok(content) => content,
271 Err(_) => r#"
272 <Neutron>
273 <Body>
274 <Container style="b:#000; w:fill; h:fill" id="Main">
275 <Text style="c:#fff; f-sz:20pt">Iris Neo di Laptop</Text>
276 <Text style="c:#0f0; f-sz:20pt">Neutron Demo</Text>
277 </Container>
278 </Body>
279 </Neutron>
280 "#
281 .to_string(),
282 };
283
284 println!("--- Neutron Engine Booting ---");
285 parse_neutron(&neutron_code);
286}
287