crml_core/selector.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/// The extracted data from the given [`Selector].
#[derive(Clone, Debug)]
pub struct SelectorState {
pub tag: String,
pub classes: Option<Vec<String>>,
pub id: Option<String>,
pub attributes: Option<Vec<String>>,
}
impl SelectorState {
/// Try to save the current buffer to the state (in the correct field).
pub fn try_save(&mut self, mode: ParserMode, mut buffer: String) -> String {
match mode {
ParserMode::None => {
if self.tag.is_empty() {
// tag cannot be overwritten
self.tag = buffer;
buffer = String::new();
}
}
ParserMode::Class => {
if self.classes.is_none() {
// classes is none; init classes with Some(vec![buffer])
self.classes = Some(vec![buffer]);
buffer = String::new();
} else if let Some(ref mut classes) = self.classes {
// classes exists; borrow as mut ref and push buffer
classes.push(buffer);
buffer = String::new();
}
}
ParserMode::Id => {
if self.id.is_none() {
// id is none; set to buffer
self.id = Some(buffer);
buffer = String::new();
}
}
ParserMode::Attribute => {
if self.attributes.is_none() {
// attributes is none; init attributes with Some(vec![buffer])
self.attributes = Some(vec![buffer]);
buffer = String::new();
} else if let Some(ref mut attributes) = self.attributes {
// attributes exists; borrow as mut ref and push buffer
attributes.push(buffer);
buffer = String::new();
}
}
}
buffer
}
/// Render state to HTML.
pub fn render(self) -> String {
let mut class_string = String::new();
let mut id_string = String::new();
let mut attributes_string = String::new();
if let Some(classes) = self.classes {
class_string = "class=\"".to_string();
for class in classes {
class_string.push_str(&(class + " "));
}
class_string += "\"";
}
if let Some(id) = self.id {
id_string = format!("id=\"{id}\"");
}
if let Some(attributes) = self.attributes {
for attribute in attributes {
attributes_string.push_str(&attribute);
}
}
format!(
"<{} {class_string} {id_string} {attributes_string}>",
self.tag
)
}
}
/// The mode of the [`Selector`] parser.
#[derive(PartialEq, Eq)]
pub enum ParserMode {
None,
Class,
Id,
Attribute,
}
/// A simple parser for CSS selectors
pub struct Selector(String);
impl Selector {
/// Create a new [`Selector`].
pub fn new(input: String) -> Self {
Self(input)
}
/// Begin parsing the selector.
pub fn parse(self) -> SelectorState {
let mut state = SelectorState {
tag: String::new(),
classes: None,
id: None,
attributes: None,
};
// parse
let mut chars = self.0.chars();
let mut mode: ParserMode = ParserMode::None;
let mut buffer: String = String::new();
while let Some(char) = chars.next() {
match char {
'.' => {
buffer = state.try_save(mode, buffer.clone());
mode = ParserMode::Class
}
'#' => {
buffer = state.try_save(mode, buffer.clone());
mode = ParserMode::Id
}
'[' => {
buffer = state.try_save(mode, buffer.clone());
mode = ParserMode::Attribute
}
']' => {
buffer = state.try_save(mode, buffer.clone());
mode = ParserMode::None
}
_ => buffer.push(char),
}
}
// return
state.try_save(mode, buffer.clone()); // one last save to catch the ending stuff
state
}
}