use alloc::borrow::ToOwned;
use alloc::string::String;
use crate::tree::{Document, NodeId, NodeKind};
pub(crate) fn name_parts(
doc: &Document,
id: NodeId,
) -> Option<(&str, Option<&str>)> {
match doc.kind(id)? {
NodeKind::Element { .. } => doc
.element_name(id)
.map(|n| (n.local.as_str(), n.namespace.as_deref())),
NodeKind::Attr(attribute) => doc
.name(attribute.name)
.map(|n| (n.local.as_str(), n.namespace.as_deref())),
NodeKind::ProcessingInstruction { target, .. } => Some((target, None)),
NodeKind::Namespace { prefix, .. } => Some((prefix, None)),
NodeKind::Root | NodeKind::Text(_) | NodeKind::Comment(_) => None,
}
}
const XML_NAMESPACE: &str = "http://www.w3.org/XML/1998/namespace";
fn attribute_by_name<'d>(
doc: &'d Document,
id: NodeId,
namespace: Option<&str>,
local: &str,
) -> Option<&'d str> {
doc.attribute_nodes(id).iter().find_map(|&a| {
let NodeKind::Attr(attribute) = doc.kind(a)? else {
return None;
};
let name = doc.name(attribute.name)?;
(name.local == local && name.namespace.as_deref() == namespace)
.then_some(attribute.value)
})
}
fn in_scope_lang(doc: &Document, id: NodeId) -> Option<&str> {
let mut at = id;
loop {
if let Some(value) =
attribute_by_name(doc, at, Some(XML_NAMESPACE), "lang")
{
return Some(value);
}
at = doc.parent(at)?;
}
}
pub(crate) fn lang_matches(doc: &Document, ctx: NodeId, want: &str) -> bool {
let Some(have) = in_scope_lang(doc, ctx) else {
return false;
};
if have.eq_ignore_ascii_case(want) {
return true;
}
have.as_bytes().get(want.len()) == Some(&b'-')
&& have
.get(..want.len())
.is_some_and(|head| head.eq_ignore_ascii_case(want))
}
pub(crate) fn qualified_name(doc: &Document, id: NodeId) -> Option<String> {
let name_id = match doc.kind(id)? {
NodeKind::Element { name, .. } => name,
NodeKind::Attr(attribute) => attribute.name,
NodeKind::ProcessingInstruction { target, .. } => {
return Some(target.to_owned());
}
NodeKind::Namespace { prefix, .. } => return Some(prefix.to_owned()),
NodeKind::Root | NodeKind::Text(_) | NodeKind::Comment(_) => {
return None;
}
};
let local = &doc.name(name_id)?.local;
Some(match doc.prefix(name_id) {
Some(prefix) => {
let mut out = String::with_capacity(prefix.len() + 1 + local.len());
out.push_str(prefix);
out.push(':');
out.push_str(local);
out
}
None => local.clone(),
})
}