use std::collections::HashMap;
pub struct RunningElementStore {
elements: HashMap<String, String>,
}
impl RunningElementStore {
pub fn new() -> Self {
Self {
elements: HashMap::new(),
}
}
pub fn register(&mut self, name: String, html: String) {
self.elements.insert(name, html);
}
pub fn get(&self, name: &str) -> Option<&str> {
self.elements.get(name).map(|s| s.as_str())
}
pub fn to_pairs(&self) -> Vec<(String, String)> {
self.elements
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
}
impl Default for RunningElementStore {
fn default() -> Self {
Self::new()
}
}
pub fn serialize_node(doc: &blitz_dom::BaseDocument, node_id: usize) -> String {
let mut output = String::new();
write_node(doc, node_id, &mut output);
output
}
fn write_node(doc: &blitz_dom::BaseDocument, node_id: usize, writer: &mut String) {
use blitz_dom::NodeData;
let Some(node) = doc.get_node(node_id) else {
return;
};
match &node.data {
NodeData::Text(text_data) => {
writer.push_str(&text_data.content);
}
NodeData::Element(elem) => {
let tag = elem.name.local.as_ref();
let has_children = !node.children.is_empty();
writer.push('<');
writer.push_str(tag);
for attr in elem.attrs() {
writer.push(' ');
writer.push_str(attr.name.local.as_ref());
writer.push_str("=\"");
escape_attribute_value(&attr.value, writer);
writer.push('"');
}
if !has_children {
writer.push_str(" />");
} else {
writer.push('>');
for &child_id in &node.children {
write_node(doc, child_id, writer);
}
writer.push_str("</");
writer.push_str(tag);
writer.push('>');
}
}
_ => {
}
}
}
fn escape_attribute_value(value: &str, writer: &mut String) {
for ch in value.chars() {
match ch {
'&' => writer.push_str("&"),
'"' => writer.push_str("""),
'<' => writer.push_str("<"),
'>' => writer.push_str(">"),
_ => writer.push(ch),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_running_element_store_and_lookup() {
let mut store = RunningElementStore::new();
assert!(store.get("header").is_none());
store.register("header".to_string(), "<h1>Title</h1>".to_string());
assert_eq!(store.get("header"), Some("<h1>Title</h1>"));
assert!(store.get("footer").is_none());
store.register("header".to_string(), "<h1>New Title</h1>".to_string());
assert_eq!(store.get("header"), Some("<h1>New Title</h1>"));
}
#[test]
fn test_to_pairs() {
let mut store = RunningElementStore::new();
store.register("header".to_string(), "<h1>Title</h1>".to_string());
store.register("footer".to_string(), "<footer>F</footer>".to_string());
let mut pairs = store.to_pairs();
pairs.sort_by(|a, b| a.0.cmp(&b.0));
assert_eq!(pairs.len(), 2);
assert_eq!(
pairs[0],
("footer".to_string(), "<footer>F</footer>".to_string())
);
assert_eq!(
pairs[1],
("header".to_string(), "<h1>Title</h1>".to_string())
);
}
}