use alloc::vec;
use core::mem;
use gramma::parse::LocationRange;
use crate::{
document::{Document, Element, ElementType, Node, NodeType, Space},
error::InternalResult,
};
const METADATA_TAGS: &[&str] = &["base", "link", "meta", "title", "style"];
fn extract_element_with_tag_in<'node, 'src, I: Iterator + Clone>(
src: &str,
node: &'node mut Node<'src>,
tags: impl IntoIterator<IntoIter = I>,
) -> Option<&'node mut Element<'src>>
where
I::Item: AsRef<str>,
{
let tags = tags.into_iter();
let e = node.as_element_mut()?;
let tag = e.selectors.first()?.tag.name()?;
if !tags
.clone()
.any(|t| tag.as_str(src).eq_ignore_ascii_case(t.as_ref()))
{
return None;
}
Some(e)
}
fn has_tag_in<'node, 'src, I: Iterator + Clone>(
src: &str,
node: &'node mut Node<'src>,
tags: impl IntoIterator<IntoIter = I>,
) -> bool
where
I::Item: AsRef<str>,
{
extract_element_with_tag_in(src, node, tags).is_some()
}
pub fn complete_page<'cfg>(mut doc: Document<'cfg>, src: &str) -> InternalResult<Document<'cfg>> {
if doc
.content
.nodes
.iter_mut()
.any(|n| has_tag_in(src, n, ["html"]))
{
return Ok(doc);
}
let mut root = Element::new(LocationRange::INVALID, ElementType::Unknown {}).with_tag("html");
root.content.range = doc.content.range;
if doc
.content
.nodes
.iter_mut()
.any(|n| has_tag_in(src, n, ["body"]))
{
root.content.nodes = doc.content.nodes
} else {
let mut head =
Element::new(LocationRange::INVALID, ElementType::Unknown {}).with_tag("head");
let mut body =
Element::new(LocationRange::INVALID, ElementType::Unknown {}).with_tag("body");
body.content.nodes = doc
.content
.nodes
.into_iter()
.filter_map(|mut n| {
if let Some(e) = extract_element_with_tag_in(src, &mut n, ["head"]) {
head.content.nodes.extend(mem::take(&mut e.content.nodes));
None
} else if has_tag_in(src, &mut n, METADATA_TAGS) {
head.content.nodes.push(n);
None
} else {
Some(n)
}
})
.collect();
root.content.nodes = vec![
head.into(),
Node {
range: LocationRange::INVALID,
node_type: NodeType::TextLike {
text_like: Space::ParagraphEnd {}.into(),
},
},
body.into(),
];
}
doc.content.nodes = vec![root.into()];
Ok(doc)
}