use crate::dom::Dom;
use crate::error::Result;
use crate::node::NodeData;
use crate::node_id::NodeId;
impl<Ext> Dom<Ext> {
pub fn text_content(&self, id: NodeId) -> String {
let mut out = String::new();
self.collect_text(id, &mut out);
out
}
fn collect_text(&self, id: NodeId, out: &mut String) {
self.walk_subtree(id, &mut |_, data| {
if let NodeData::Text { data } = data {
out.push_str(data);
}
});
}
}
impl<Ext: Default> Dom<Ext> {
pub fn set_text_content(&mut self, id: NodeId, text: &str) -> Result<()> {
use crate::error::DomError;
use crate::node::NodeType;
match &self.node_or_err(id)?.data {
NodeData::Element { .. } | NodeData::Fragment => {}
NodeData::Text { .. } => {
return Err(DomError::WrongNodeType {
expected: "Element or Fragment",
got: NodeType::Text,
});
}
NodeData::Comment { .. } => {
return Err(DomError::WrongNodeType {
expected: "Element or Fragment",
got: NodeType::Comment,
});
}
}
let existing: Vec<NodeId> = {
let mut out = Vec::new();
let mut c = self.get_node(id).and_then(|n| n.first_child);
while let Some(cid) = c {
out.push(cid);
c = self.get_node(cid).and_then(|n| n.next_sibling);
}
out
};
for cid in existing {
self.drop_subtree(cid)?;
}
if !text.is_empty() {
let t = self.create_text_node(text);
self.append_child(id, t)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::Dom;
#[test]
fn text_node_returns_own_data() {
let mut dom: Dom = Dom::new();
let t = dom.create_text_node("hello");
assert_eq!(dom.text_content(t), "hello");
}
#[test]
fn element_concatenates_descendant_text() {
let mut dom: Dom = Dom::new();
let div = dom.create_element("div");
let a = dom.create_text_node("hello ");
let span = dom.create_element("span");
let b = dom.create_text_node("world");
dom.append_child(span, b).unwrap();
dom.append_child(div, a).unwrap();
dom.append_child(div, span).unwrap();
assert_eq!(dom.text_content(div), "hello world");
}
#[test]
fn comment_children_not_included() {
let mut dom: Dom = Dom::new();
let div = dom.create_element("div");
let a = dom.create_text_node("before ");
let c = dom.create_comment(" skip me ");
let b = dom.create_text_node("after");
dom.append_child(div, a).unwrap();
dom.append_child(div, c).unwrap();
dom.append_child(div, b).unwrap();
assert_eq!(dom.text_content(div), "before after");
}
#[test]
fn empty_element_empty_text_content() {
let mut dom: Dom = Dom::new();
let div = dom.create_element("div");
assert_eq!(dom.text_content(div), "");
}
#[test]
fn set_text_content_replaces_children() {
let mut dom: Dom = Dom::new();
let div = dom.create_element("div");
let old_text = dom.create_text_node("old");
let old_span = dom.create_element("span");
dom.append_child(div, old_text).unwrap();
dom.append_child(div, old_span).unwrap();
dom.set_text_content(div, "new content").unwrap();
assert_eq!(dom.node(div).child_element_count(), 0);
assert_eq!(
dom.node(div).first_child().unwrap().node_value(),
Some("new content")
);
assert_eq!(dom.text_content(div), "new content");
}
#[test]
fn set_text_content_empty_clears_children() {
let mut dom: Dom = Dom::new();
let div = dom.create_element("div");
let t = dom.create_text_node("existing");
dom.append_child(div, t).unwrap();
dom.set_text_content(div, "").unwrap();
assert!(!dom.node(div).has_child_nodes());
}
#[test]
fn set_text_content_on_text_errors() {
let mut dom: Dom = Dom::new();
let t = dom.create_text_node("hi");
assert!(dom.set_text_content(t, "bye").is_err());
}
#[test]
fn set_text_content_on_fragment_works() {
let mut dom: Dom = Dom::new();
let frag = dom.create_document_fragment();
let old = dom.create_element("span");
dom.append_child(frag, old).unwrap();
dom.set_text_content(frag, "flat text").unwrap();
assert_eq!(dom.text_content(frag), "flat text");
}
}