use super::cursor::Cursor;
use super::{Attribute, TagItem};
use crate::ast::Value;
impl Cursor<'_> {
pub(crate) fn top(&mut self) -> Option<TagItem> {
if let Some(item) = self.top_level_value() {
return Some(item);
}
if let Some(item) = self.annotation() {
return Some(item);
}
if let Some(item) = self.tag_open() {
return Some(item);
}
self.tag_close()
}
fn top_level_value(&mut self) -> Option<TagItem> {
if let Some(variable) = self.variable() {
return Some(TagItem::Variable(variable));
}
let function = self.function()?;
Some(TagItem::Variable(function))
}
fn annotation(&mut self) -> Option<TagItem> {
let start = self.pos();
let Some(attributes) = self.tag_attributes() else {
self.reset(start);
return None;
};
self.whitespace_star();
Some(TagItem::Annotation { attributes })
}
fn tag_open(&mut self) -> Option<TagItem> {
let start = self.pos();
let Some(name) = self.tag_name() else {
self.reset(start);
return None;
};
self.whitespace_star();
let primary = {
let mark = self.pos();
if let Some(value) = self.value() {
self.whitespace();
Some(value)
} else {
self.reset(mark);
None
}
};
let mut attributes = self.tag_attributes().unwrap_or_default();
self.whitespace_star();
let self_closing = self.literal("/");
if let Some(primary) = primary
&& primary.is_truthy()
{
attributes.insert(
0,
Attribute::Attribute {
name: "primary".to_string(),
value: primary,
},
);
}
Some(TagItem::TagOpen {
name,
attributes,
self_closing,
})
}
fn tag_close(&mut self) -> Option<TagItem> {
let start = self.pos();
if !self.literal("/") {
self.reset(start);
return None;
}
let Some(name) = self.tag_name() else {
self.reset(start);
return None;
};
Some(TagItem::TagClose { name })
}
fn tag_name(&mut self) -> Option<String> {
self.enter_named("tag name");
let name = self.identifier().map(ToString::to_string);
self.leave_named();
name
}
fn tag_attributes(&mut self) -> Option<Vec<Attribute>> {
let head = self.tag_attributes_item()?;
let mut items = vec![head];
loop {
let mark = self.pos();
if !self.whitespace_plus() {
self.reset(mark);
break;
}
let Some(item) = self.tag_attributes_item() else {
self.reset(mark);
break;
};
items.push(item);
}
Some(items)
}
fn tag_attributes_item(&mut self) -> Option<Attribute> {
if let Some(item) = self.tag_shortcut_id() {
return Some(item);
}
if let Some(item) = self.tag_shortcut_class() {
return Some(item);
}
self.tag_attribute()
}
fn tag_shortcut_id(&mut self) -> Option<Attribute> {
let start = self.pos();
self.enter_named("id");
let name = if self.literal("#") {
self.identifier()
} else {
None
};
self.leave_named();
if let Some(name) = name {
return Some(Attribute::Attribute {
name: "id".to_string(),
value: Value::String(name.to_string()),
});
}
self.reset(start);
None
}
fn tag_shortcut_class(&mut self) -> Option<Attribute> {
let start = self.pos();
self.enter_named("class");
let name = if self.literal(".") {
self.identifier()
} else {
None
};
self.leave_named();
if let Some(name) = name {
return Some(Attribute::Class {
name: name.to_string(),
});
}
self.reset(start);
None
}
fn tag_attribute(&mut self) -> Option<Attribute> {
let start = self.pos();
let Some(name) = self.identifier() else {
self.reset(start);
return None;
};
if !self.literal("=") {
self.reset(start);
return None;
}
let Some(value) = self.value() else {
self.reset(start);
return None;
};
Some(Attribute::Attribute {
name: name.to_string(),
value,
})
}
}