#[path = "iris/mod.rs"]
pub mod iris;
use std::fs;
#[derive(Debug, Clone, Copy)]
pub struct StyleDecl<'a> {
pub name: &'a str,
pub value: &'a str,
}
#[derive(Debug, Clone, Copy)]
pub struct Attribute<'a> {
pub name: &'a str,
pub value: &'a str,
}
#[derive(Debug)]
pub enum StyleProp<'a> {
Background(&'a str),
Color(&'a str),
Width(&'a str),
Height(&'a str),
FontSize(&'a str),
Raw(&'a str, &'a str),
}
pub fn parse_tag<'a>(input: &'a str) -> Option<(&'a str, &'a str)> {
let input = input.trim();
if !input.starts_with('<') || !input.ends_with('>') {
return None;
}
let inner = &input[1..input.len() - 1];
let mut parts = inner.splitn(2, char::is_whitespace);
let tag_name = parts.next()?.trim();
let attrs = parts.next().unwrap_or("").trim();
Some((tag_name, attrs))
}
pub fn parse_attributes<'a>(input: &'a str, out: &mut [Attribute<'a>]) -> usize {
let mut count = 0;
let mut i = 0;
let bytes = input.as_bytes();
while i < bytes.len() && count < out.len() {
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() {
break;
}
let start = i;
while i < bytes.len() && bytes[i] != b'=' && !bytes[i].is_ascii_whitespace() {
i += 1;
}
let name = &input[start..i].trim();
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() || bytes[i] != b'=' {
break;
}
i += 1;
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() || bytes[i] != b'"' {
break;
}
i += 1;
let value_start = i;
while i < bytes.len() && bytes[i] != b'"' {
i += 1;
}
if i > bytes.len() {
break;
}
let value = &input[value_start..i];
i += 1;
out[count] = Attribute { name, value };
count += 1;
}
count
}
pub fn parse_style<'a>(input: &'a str, out: &mut [StyleDecl<'a>]) -> usize {
let mut count = 0;
let mut rest = input;
while !rest.is_empty() && count < out.len() {
let semicolon = rest.find(';').unwrap_or(rest.len());
let decl = rest[..semicolon].trim();
if !decl.is_empty() {
if let Some((name, value)) = decl.split_once(':') {
let name = name.trim();
let value = value.trim();
if !name.is_empty() && !value.is_empty() {
out[count] = StyleDecl { name, value };
count += 1;
}
}
}
rest = if semicolon < rest.len() {
&rest[semicolon + 1..]
} else {
""
};
}
count
}
pub fn normalize_decl<'a>(decl: StyleDecl<'a>) -> StyleProp<'a> {
match decl.name {
"b" => StyleProp::Background(decl.value),
"c" => StyleProp::Color(decl.value),
"w" => StyleProp::Width(decl.value),
"h" => StyleProp::Height(decl.value),
"f-sz" => StyleProp::FontSize(decl.value),
_ => StyleProp::Raw(decl.name, decl.value),
}
}
pub fn parse_neutron(input: &str) {
let mut attrs = [Attribute { name: "", value: "" }; 12];
let mut styles = [StyleDecl { name: "", value: "" }; 16];
let mut i = 0;
while let Some(start) = input[i..].find('<') {
let start = i + start;
if let Some(end) = input[start..].find('>') {
let end = start + end + 1;
let tag_content = &input[start..end];
if let Some((tag_name, attrs_text)) = parse_tag(tag_content) {
println!("[Tag] {}", tag_name);
let attr_count = parse_attributes(attrs_text, &mut attrs);
for attr in &attrs[..attr_count] {
println!(" [Attr] {} = \"{}\"", attr.name, attr.value);
if attr.name == "style" {
let style_count = parse_style(attr.value, &mut styles);
for style in &styles[..style_count] {
let prop = normalize_decl(*style);
println!(" [Style] {:?}", prop);
}
}
}
}
i = end;
} else {
break;
}
}
}
pub fn parse_neutron_file(path: &str) {
let neutron_code = match fs::read_to_string(path) {
Ok(content) => content,
Err(_) => r#"
<Neutron>
<Body>
<Container style="b:#000; w:fill; h:fill" id="Main">
<Text style="c:#fff; f-sz:20pt">Iris Neo di Laptop</Text>
<Text style="c:#0f0; f-sz:20pt">Neutron Demo</Text>
</Container>
</Body>
</Neutron>
"#
.to_string(),
};
println!("--- Neutron Engine Booting ---");
parse_neutron(&neutron_code);
}