use ahash::{HashMap, HashMapExt};
use std::sync::LazyLock;
pub const FN_NAMESPACE: &str = "http://www.w3.org/2005/xpath-functions";
pub const XS_NAMESPACE: &str = "http://www.w3.org/2001/XMLSchema";
const XML_NAMESPACE: &str = "http://www.w3.org/XML/1998/namespace";
const STATIC_NAMESPACES: [(&str, &str); 7] = [
("xs", XS_NAMESPACE),
("fn", FN_NAMESPACE),
("math", "http://www.w3.org/2005/xpath-functions/math"),
("map", "http://www.w3.org/2005/xpath-functions/map"),
("array", "http://www.w3.org/2005/xpath-functions/array"),
("err", "http://www.w3.org/2005/xqt-errors"),
("output", "http://www.w3.org/2010/xslt-xquery-serialization"),
];
pub static DEFAULT_NAMESPACES: LazyLock<Namespaces> = LazyLock::new(|| Default::default());
#[derive(Debug, Clone)]
pub struct Namespaces {
namespaces: HashMap<String, String>,
pub default_element_namespace: String,
pub default_function_namespace: String,
}
impl Namespaces {
pub const FN_NAMESPACE: &'static str = FN_NAMESPACE;
pub fn new(
namespaces: HashMap<String, String>,
default_element_namespace: String,
default_function_namespace: String,
) -> Self {
Self {
namespaces,
default_element_namespace,
default_function_namespace,
}
}
pub fn default_namespaces() -> HashMap<String, String> {
let mut namespaces = HashMap::new();
namespaces.insert("xml".to_string(), XML_NAMESPACE.to_string());
for (prefix, uri) in STATIC_NAMESPACES.into_iter() {
namespaces.insert(prefix.to_string(), uri.to_string());
}
namespaces
}
pub fn add(&mut self, namespace_pairs: &[(&str, &str)]) {
for (prefix, namespace) in namespace_pairs {
if prefix.is_empty() {
self.default_element_namespace = namespace.to_string();
} else {
self.namespaces
.insert(prefix.to_string(), namespace.to_string());
}
}
}
#[inline]
pub fn by_prefix(&self, prefix: &str) -> Option<&str> {
self.namespaces.get(prefix).map(String::as_str)
}
#[inline]
pub fn default_element_namespace(&self) -> &str {
self.default_element_namespace.as_str()
}
}
impl Default for Namespaces {
fn default() -> Self {
Self::new(
Self::default_namespaces(),
"".to_string(),
FN_NAMESPACE.to_string(),
)
}
}
pub trait NamespaceLookup {
fn by_prefix(&self, prefix: &str) -> Option<&str>;
}
impl NamespaceLookup for Namespaces {
fn by_prefix(&self, prefix: &str) -> Option<&str> {
self.namespaces.get(prefix).map(String::as_str)
}
}
impl<T: NamespaceLookup> NamespaceLookup for &T {
fn by_prefix(&self, prefix: &str) -> Option<&str> {
(**self).by_prefix(prefix)
}
}