oa_gateway_uci/xsd/node_util.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
use roxmltree::Node;
use crate::UciError;
/// The XML Schema namespace. Documents are free to bind it to any prefix.
pub(super) const XS: &str = "http://www.w3.org/2001/XMLSchema";
/// Child elements that carry structure, with documentation skipped.
///
/// Two thirds of the published schema is `xs:annotation`, so dropping it here
/// keeps every caller from having to.
pub(super) fn definitions<'a, 'i>(
node: Node<'a, 'i>,
) -> Result<impl Iterator<Item = Node<'a, 'i>>, UciError> {
for child in node.children().filter(Node::is_element) {
if !is_xs(child) {
return Err(UciError::Xsd(format!(
"unexpected element <{}> outside the XML Schema namespace",
child.tag_name().name()
)));
}
}
Ok(node
.children()
.filter(|c| c.is_element() && c.tag_name().name() != "annotation"))
}
/// Whether `node` is in the XML Schema namespace.
pub(super) fn is_xs(node: Node<'_, '_>) -> bool {
node.tag_name().namespace() == Some(XS)
}
/// Required attribute, or an XSD error naming the element.
pub(super) fn required<'a>(node: Node<'a, '_>, attr: &str) -> Result<&'a str, UciError> {
node.attribute(attr).ok_or_else(|| {
UciError::Xsd(format!(
"<xs:{}> is missing the required {attr}= attribute",
node.tag_name().name()
))
})
}
/// Normalize a QName into the form the rest of the crate expects.
///
/// XML Schema primitives become `xs:name` regardless of the prefix the document
/// binds them to, and names in any other namespace reduce to their local part.
/// Reducing is safe because UCI defines everything in a single target namespace;
/// the prefix cannot be stripped blindly, since that would turn `xs:string` into
/// `string` and defeat every leaf coercion.
pub(super) fn type_ref(node: Node<'_, '_>, qname: &str) -> String {
let (prefix, local) = match qname.split_once(':') {
Some((p, l)) => (Some(p), l),
None => (None, qname),
};
if node.lookup_namespace_uri(prefix) == Some(XS) {
format!("xs:{local}")
} else {
local.to_owned()
}
}