use alloc::string::String;
use alloc::vec::Vec;
use super::Value;
use crate::tree::ExpandedName;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct EvalLimits {
pub max_work: usize,
pub max_nodes: usize,
pub max_string_bytes: usize,
}
impl Default for EvalLimits {
fn default() -> Self {
Self {
max_work: 10_000_000,
max_nodes: 100_000,
max_string_bytes: 1024 * 1024,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct EvalContext {
variables: Vec<(ExpandedName, Value)>,
pub(crate) limits: Option<EvalLimits>,
}
impl EvalContext {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_limits(mut self, limits: EvalLimits) -> Self {
self.limits = Some(limits);
self
}
#[must_use]
pub fn bind(mut self, name: ExpandedName, value: Value) -> Self {
match self.variables.iter_mut().find(|(n, _)| *n == name) {
Some(slot) => slot.1 = value,
None => self.variables.push((name, value)),
}
self
}
pub(crate) fn binding_count(&self) -> usize {
self.variables.len()
}
#[must_use]
pub fn variable(&self, name: &ExpandedName) -> Option<&Value> {
self.variables
.iter()
.find(|(n, _)| n == name)
.map(|(_, v)| v)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EvalError {
UnboundVariable(String),
TypeError(String),
Stale,
LimitExceeded(&'static str),
}
impl core::fmt::Display for EvalError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnboundVariable(name) => {
write!(f, "unbound variable `${name}`")
}
Self::TypeError(message) => f.write_str(message),
Self::Stale => f.write_str("the context node has been removed"),
Self::LimitExceeded(resource) => {
write!(f, "XPath evaluation exceeds the {resource} limit")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for EvalError {}