//! Plain-text serialization of a Document for inspection and assertions. use we_dom::{Document, NodeData, NodeId}; /// Dump the document tree as indented text. Useful as a "what does the parser /// think this page is" snapshot for regression tests. pub fn dump(doc: &Document) -> String { let mut out = String::new(); walk(doc, doc.root(), 0, &mut out); out } fn walk(doc: &Document, node: NodeId, depth: usize, out: &mut String) { for _ in 0..depth { out.push_str(" "); } match doc.node_data(node) { NodeData::Document => out.push_str("#document\n"), NodeData::DocumentFragment => out.push_str("#document-fragment\n"), NodeData::Element { tag_name, .. } => { out.push('<'); out.push_str(tag_name.as_str()); if let Some(attrs) = doc.attributes(node) { for a in attrs { out.push(' '); out.push_str(a.name.as_str()); out.push_str("=\""); for c in a.value.chars() { match c { '"' => out.push_str("""), '\n' => out.push_str("\\n"), _ => out.push(c), } } out.push('"'); } } out.push_str(">\n"); // Descend into a declarative shadow root, if present, so shadow // content appears in the dump (it is not a light-DOM child). if let Some(root) = doc.shadow_root(node) { for _ in 0..depth + 1 { out.push_str(" "); } out.push_str("#shadow-root\n"); for child in doc.children(root) { walk(doc, child, depth + 2, out); } } } NodeData::Text { data } => { let trimmed = data.trim(); if !trimmed.is_empty() { out.push('"'); for c in trimmed.chars().take(120) { match c { '"' => out.push_str("\\\""), '\n' => out.push_str("\\n"), _ => out.push(c), } } if trimmed.chars().count() > 120 { out.push('…'); } out.push('"'); out.push('\n'); } else { out.push_str("(whitespace)\n"); } } NodeData::Comment { data } => { out.push_str("\n"); } } for child in doc.children(node) { walk(doc, child, depth + 1, out); } }