1pub mod iris;
33
34use std::fs;
35
36#[derive(Debug, Clone, Copy)]
38pub struct StyleDecl<'a> {
39 pub name: &'a str,
40 pub value: &'a str,
41}
42
43#[derive(Debug, Clone, Copy)]
45pub struct Attribute<'a> {
46 pub name: &'a str,
47 pub value: &'a str,
48}
49
50#[derive(Debug)]
52pub enum StyleProp<'a> {
53 Background(&'a str),
54 Color(&'a str),
55 Width(&'a str),
56 Height(&'a str),
57 FontSize(&'a str),
58 Raw(&'a str, &'a str),
59}
60
61pub fn parse_tag<'a>(input: &'a str) -> Option<(&'a str, &'a str)> {
72 let input = input.trim();
73 if !input.starts_with('<') || !input.ends_with('>') {
74 return None;
75 }
76 let inner = &input[1..input.len()-1];
77 let mut parts = inner.splitn(2, char::is_whitespace);
78 let tag_name = parts.next()?.trim();
79 let attrs = parts.next().unwrap_or("").trim();
80 Some((tag_name, attrs))
81}
82
83pub fn parse_attributes<'a>(input: &'a str, out: &mut [Attribute<'a>]) -> usize {
97 let mut count = 0;
98 let mut i = 0;
99 let bytes = input.as_bytes();
100
101 while i < bytes.len() && count < out.len() {
102 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
103 i += 1;
104 }
105 if i >= bytes.len() {
106 break;
107 }
108
109 let start = i;
110 while i < bytes.len() && bytes[i] != b'=' && !bytes[i].is_ascii_whitespace() {
111 i += 1;
112 }
113 let name = &input[start..i].trim();
114
115 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
116 i += 1;
117 }
118 if i >= bytes.len() || bytes[i] != b'=' {
119 break;
120 }
121 i += 1;
122
123 while i < bytes.len() && bytes[i].is_ascii_whitespace() {
124 i += 1;
125 }
126 if i >= bytes.len() || bytes[i] != b'"' {
127 break;
128 }
129 i += 1;
130 let value_start = i;
131
132 while i < bytes.len() && bytes[i] != b'"' {
133 i += 1;
134 }
135 if i > bytes.len() {
136 break;
137 }
138 let value = &input[value_start..i];
139 i += 1;
140
141 out[count] = Attribute { name, value };
142 count += 1;
143 }
144
145 count
146}
147
148pub fn parse_style<'a>(input: &'a str, out: &mut [StyleDecl<'a>]) -> usize {
162 let mut count = 0;
163 let mut rest = input;
164
165 while !rest.is_empty() && count < out.len() {
166 let semicolon = rest.find(';').unwrap_or(rest.len());
167 let decl = rest[..semicolon].trim();
168
169 if !decl.is_empty() {
170 if let Some((name, value)) = decl.split_once(':') {
171 let name = name.trim();
172 let value = value.trim();
173 if !name.is_empty() && !value.is_empty() {
174 out[count] = StyleDecl { name, value };
175 count += 1;
176 }
177 }
178 }
179
180 rest = if semicolon < rest.len() {
181 &rest[semicolon + 1..]
182 } else {
183 ""
184 }
185 }
186
187 count
188}
189
190pub fn normalize_decl<'a>(decl: StyleDecl<'a>) -> StyleProp<'a> {
206 match decl.name {
207 "b" => StyleProp::Background(decl.value),
208 "c" => StyleProp::Color(decl.value),
209 "w" => StyleProp::Width(decl.value),
210 "h" => StyleProp::Height(decl.value),
211 "f-sz" => StyleProp::FontSize(decl.value),
212 _ => StyleProp::Raw(decl.name, decl.value),
213 }
214}
215
216pub fn parse_neutron(input: &str) {
220 let mut attrs = [Attribute { name: "", value: "" }; 12];
221 let mut styles = [StyleDecl { name: "", value: "" }; 16];
222
223 let mut i = 0;
224 while let Some(start) = input[i..].find('<') {
225 let start = i + start;
226 if let Some(end) = input[start..].find('>') {
227 let end = start + end + 1;
228 let tag_content = &input[start..end];
229
230 if let Some((tag_name, attrs_text)) = parse_tag(tag_content) {
231 println!("[Tag] {}", tag_name);
232
233 let attr_count = parse_attributes(attrs_text, &mut attrs);
234 for attr in &attrs[..attr_count] {
235 println!(" [Attr] {} = \"{}\"", attr.name, attr.value);
236
237 if attr.name == "style" {
238 let style_count = parse_style(attr.value, &mut styles);
239 for style in &styles[..style_count] {
240 let prop = normalize_decl(*style);
241 println!(" [Style] {:?}", prop);
242 }
243 }
244 }
245 }
246
247 i = end;
248 } else {
249 break;
250 }
251 }
252}
253
254pub fn parse_neutron_file(path: &str) {
264 let neutron_code = match fs::read_to_string(path) {
265 Ok(content) => content,
266 Err(_) => r#"
267 <Neutron>
268 <Body>
269 <Container style="b:#000; w:fill; h:fill" id="Main">
270 <Text style="c:#fff; f-sz:20pt">Iris Neo di Laptop</Text>
271 <Text style="c:#0f0; f-sz:20pt">Neutron Demo</Text>
272 </Container>
273 </Body>
274 </Neutron>
275 "#.to_string(),
276 };
277
278 println!("--- Neutron Engine Booting ---");
279 parse_neutron(&neutron_code);
280}