use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use crate::tree::ExpandedName;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Axis {
Child,
Descendant,
DescendantOrSelf,
Parent,
Ancestor,
AncestorOrSelf,
SelfAxis,
Attribute,
FollowingSibling,
PrecedingSibling,
Following,
Preceding,
Namespace,
}
impl Axis {
#[must_use]
pub const fn is_reverse(self) -> bool {
matches!(
self,
Self::Ancestor
| Self::AncestorOrSelf
| Self::Preceding
| Self::PrecedingSibling
)
}
pub const ALL: [Self; 13] = [
Self::Child,
Self::Descendant,
Self::DescendantOrSelf,
Self::Parent,
Self::Ancestor,
Self::AncestorOrSelf,
Self::SelfAxis,
Self::Attribute,
Self::FollowingSibling,
Self::PrecedingSibling,
Self::Following,
Self::Preceding,
Self::Namespace,
];
#[must_use]
pub const fn name(self) -> &'static str {
const NAMES: [&str; 13] = [
"child",
"descendant",
"descendant-or-self",
"parent",
"ancestor",
"ancestor-or-self",
"self",
"attribute",
"following-sibling",
"preceding-sibling",
"following",
"preceding",
"namespace",
];
NAMES[self as usize]
}
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
Self::ALL.into_iter().find(|a| a.name() == name)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeTest {
Wildcard,
Name {
namespace: Option<String>,
local: String,
},
AnyNamespace {
local: String,
},
Text,
Comment,
ProcessingInstruction(Option<String>),
Any,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Step {
pub axis: Axis,
pub test: NodeTest,
pub predicates: Vec<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Path {
absolute: bool,
steps: Vec<Step>,
},
Literal(String),
Number(f64),
Binary {
op: BinaryOp,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Chain {
first: Box<Expr>,
rest: Vec<(BinaryOp, Expr)>,
},
Function {
func: Func,
args: Vec<Expr>,
},
Negate(Box<Expr>),
Variable(Box<ExpandedName>),
Filter(Box<Filter>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Filter {
pub base: Expr,
pub predicates: Vec<Expr>,
pub steps: Vec<Step>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Func {
Last,
Position,
Count,
Id,
LocalName,
NamespaceUri,
Name,
String,
Concat,
StartsWith,
Contains,
SubstringBefore,
SubstringAfter,
Substring,
StringLength,
NormalizeSpace,
Translate,
Boolean,
Not,
True,
False,
Lang,
Number,
Sum,
Floor,
Ceiling,
Round,
}
impl Func {
pub const ALL: [Self; 27] = [
Self::Last,
Self::Position,
Self::Count,
Self::Id,
Self::LocalName,
Self::NamespaceUri,
Self::Name,
Self::String,
Self::Concat,
Self::StartsWith,
Self::Contains,
Self::SubstringBefore,
Self::SubstringAfter,
Self::Substring,
Self::StringLength,
Self::NormalizeSpace,
Self::Translate,
Self::Boolean,
Self::Not,
Self::True,
Self::False,
Self::Lang,
Self::Number,
Self::Sum,
Self::Floor,
Self::Ceiling,
Self::Round,
];
#[must_use]
pub const fn name(self) -> &'static str {
self.spec().0
}
#[must_use]
pub const fn arity(self) -> (usize, usize) {
let (_, min, max) = self.spec();
(min, max)
}
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
Self::ALL.into_iter().find(|f| f.name() == name)
}
const fn spec(self) -> (&'static str, usize, usize) {
const SPECS: [(&str, usize, usize); 27] = [
("last", 0, 0),
("position", 0, 0),
("count", 1, 1),
("id", 1, 1),
("local-name", 0, 1),
("namespace-uri", 0, 1),
("name", 0, 1),
("string", 0, 1),
("concat", 2, usize::MAX),
("starts-with", 2, 2),
("contains", 2, 2),
("substring-before", 2, 2),
("substring-after", 2, 2),
("substring", 2, 3),
("string-length", 0, 1),
("normalize-space", 0, 1),
("translate", 3, 3),
("boolean", 1, 1),
("not", 1, 1),
("true", 0, 0),
("false", 0, 0),
("lang", 1, 1),
("number", 0, 1),
("sum", 1, 1),
("floor", 1, 1),
("ceiling", 1, 1),
("round", 1, 1),
];
SPECS[self as usize]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryOp {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Add,
Sub,
Mul,
Div,
Mod,
Union,
}
impl BinaryOp {
#[must_use]
pub const fn symbol(self) -> &'static str {
const SYMBOLS: [&str; 14] = [
"=", "!=", "<", "<=", ">", ">=", "and", "or", "+", "-", "*", "div",
"mod", "|",
];
SYMBOLS[self as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expr_stays_small() {
assert!(core::mem::size_of::<Expr>() <= 40);
}
}