Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
788 lines
27 KiB
Rust
788 lines
27 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use super::{
|
|
BinaryOperator, Constant, Expression, ExpressionItem, StringCow, SystemVariable, UnaryOperator,
|
|
Variable,
|
|
functions::{FUNCTIONS, ResolveVariable},
|
|
if_block::IfBlock,
|
|
};
|
|
use crate::Server;
|
|
use compact_str::{CompactString, ToCompactString, format_compact};
|
|
use hyper::StatusCode;
|
|
use registry::{
|
|
schema::prelude::Property,
|
|
types::{EnumImpl, id::ObjectId},
|
|
};
|
|
use std::{cmp::Ordering, fmt::Display};
|
|
use trc::{Collector, EvalEvent};
|
|
|
|
impl Server {
|
|
pub async fn eval_if<'x, R: TryFrom<Variable<'x>>, V: ResolveVariable>(
|
|
&'x self,
|
|
if_block: &'x IfBlock,
|
|
resolver: &'x V,
|
|
session_id: u64,
|
|
) -> Option<R> {
|
|
if if_block.is_empty() {
|
|
trc::event!(
|
|
Eval(EvalEvent::Result),
|
|
SpanId = session_id,
|
|
Id = if_block.id.to_string(),
|
|
Key = if_block.property.as_str(),
|
|
Result = ""
|
|
);
|
|
|
|
return None;
|
|
}
|
|
|
|
match (EvalContext {
|
|
resolver,
|
|
core: self,
|
|
expr: if_block,
|
|
captures: Vec::new(),
|
|
session_id,
|
|
})
|
|
.eval()
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Result),
|
|
SpanId = session_id,
|
|
Id = if_block.id.to_string(),
|
|
Key = if_block.property.as_str(),
|
|
Result = format!("{result:?}"),
|
|
);
|
|
|
|
match result.try_into() {
|
|
Ok(value) => Some(value),
|
|
Err(_) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Result),
|
|
SpanId = session_id,
|
|
Id = if_block.id.to_string(),
|
|
Key = if_block.property.as_str(),
|
|
Result = "",
|
|
);
|
|
|
|
None
|
|
}
|
|
}
|
|
}
|
|
Err(err) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Error),
|
|
SpanId = session_id,
|
|
Id = if_block.id.to_string(),
|
|
Key = if_block.property.as_str(),
|
|
CausedBy = err,
|
|
);
|
|
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn eval_expr<'x, R: TryFrom<Variable<'x>>, V: ResolveVariable>(
|
|
&'x self,
|
|
expr: &'x Expression,
|
|
resolver: &'x V,
|
|
obj_id: ObjectId,
|
|
property: Property,
|
|
session_id: u64,
|
|
) -> Option<R> {
|
|
if expr.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
match (EvalContext {
|
|
resolver,
|
|
core: self,
|
|
expr,
|
|
captures: &mut Vec::new(),
|
|
session_id,
|
|
})
|
|
.eval()
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Result),
|
|
SpanId = session_id,
|
|
Id = obj_id.to_string(),
|
|
Key = property.as_str(),
|
|
Result = format!("{result:?}"),
|
|
);
|
|
|
|
match result.try_into() {
|
|
Ok(value) => Some(value),
|
|
Err(_) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Error),
|
|
SpanId = session_id,
|
|
Id = obj_id.to_string(),
|
|
Key = property.as_str(),
|
|
Details = "Failed to convert result",
|
|
);
|
|
|
|
None
|
|
}
|
|
}
|
|
}
|
|
Err(err) => {
|
|
trc::event!(
|
|
Eval(EvalEvent::Error),
|
|
SpanId = session_id,
|
|
Id = obj_id.to_string(),
|
|
Key = property.as_str(),
|
|
CausedBy = err,
|
|
);
|
|
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct EvalContext<'x, V: ResolveVariable, T, C> {
|
|
resolver: &'x V,
|
|
core: &'x Server,
|
|
expr: &'x T,
|
|
captures: C,
|
|
session_id: u64,
|
|
}
|
|
|
|
impl<'x, V: ResolveVariable> EvalContext<'x, V, IfBlock, Vec<CompactString>> {
|
|
async fn eval(&mut self) -> trc::Result<Variable<'x>> {
|
|
for if_then in &self.expr.if_then {
|
|
if (EvalContext {
|
|
resolver: self.resolver,
|
|
core: self.core,
|
|
expr: &if_then.expr,
|
|
captures: &mut self.captures,
|
|
session_id: self.session_id,
|
|
})
|
|
.eval()
|
|
.await?
|
|
.to_bool()
|
|
{
|
|
return (EvalContext {
|
|
resolver: self.resolver,
|
|
core: self.core,
|
|
expr: &if_then.then,
|
|
captures: &mut self.captures,
|
|
session_id: self.session_id,
|
|
})
|
|
.eval()
|
|
.await;
|
|
}
|
|
}
|
|
|
|
(EvalContext {
|
|
resolver: self.resolver,
|
|
core: self.core,
|
|
expr: &self.expr.default,
|
|
captures: &mut self.captures,
|
|
session_id: self.session_id,
|
|
})
|
|
.eval()
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl<'x, V: ResolveVariable> EvalContext<'x, V, Expression, &mut Vec<CompactString>> {
|
|
async fn eval(&mut self) -> trc::Result<Variable<'x>> {
|
|
let mut stack = Vec::new();
|
|
let mut exprs = self.expr.items.iter();
|
|
|
|
while let Some(expr) = exprs.next() {
|
|
match expr {
|
|
ExpressionItem::Variable(v) => {
|
|
stack.push(self.resolver.resolve_variable(*v));
|
|
}
|
|
ExpressionItem::Global(v) => {
|
|
stack.push(self.resolver.resolve_global(v));
|
|
}
|
|
ExpressionItem::Constant(val) => {
|
|
stack.push(Variable::from(val));
|
|
}
|
|
ExpressionItem::Capture(v) => {
|
|
stack.push(Variable::String(StringCow::Owned(
|
|
self.captures
|
|
.get(*v as usize)
|
|
.map(|v| v.as_str())
|
|
.unwrap_or_default()
|
|
.to_compact_string(),
|
|
)));
|
|
}
|
|
ExpressionItem::System(setting) => match setting {
|
|
SystemVariable::Hostname => {
|
|
stack.push(self.core.core.network.server_name.as_str().into())
|
|
}
|
|
SystemVariable::Domain => {
|
|
stack.push(self.core.core.email.default_domain_name.as_str().into())
|
|
}
|
|
SystemVariable::NodeId => stack.push(self.core.core.network.node_id.into()),
|
|
SystemVariable::NodeHostname => {
|
|
stack.push(self.core.registry().local_hostname().into())
|
|
}
|
|
SystemVariable::NodeRole => stack.push(
|
|
self.core
|
|
.registry()
|
|
.cluster_role()
|
|
.unwrap_or_default()
|
|
.into(),
|
|
),
|
|
SystemVariable::Metric(variable) => {
|
|
stack.push(Variable::Float(Collector::read_metric(*variable)));
|
|
}
|
|
},
|
|
ExpressionItem::UnaryOperator(op) => {
|
|
let value = stack.pop().unwrap_or_default();
|
|
stack.push(match op {
|
|
UnaryOperator::Not => value.op_not(),
|
|
UnaryOperator::Minus => value.op_minus(),
|
|
});
|
|
}
|
|
ExpressionItem::BinaryOperator(op) => {
|
|
let right = stack.pop().unwrap_or_default();
|
|
let left = stack.pop().unwrap_or_default();
|
|
stack.push(match op {
|
|
BinaryOperator::Add => left.op_add(right),
|
|
BinaryOperator::Subtract => left.op_subtract(right),
|
|
BinaryOperator::Multiply => left.op_multiply(right),
|
|
BinaryOperator::Divide => left.op_divide(right),
|
|
BinaryOperator::And => left.op_and(right),
|
|
BinaryOperator::Or => left.op_or(right),
|
|
BinaryOperator::Xor => left.op_xor(right),
|
|
BinaryOperator::Eq => left.op_eq(right),
|
|
BinaryOperator::Ne => left.op_ne(right),
|
|
BinaryOperator::Lt => left.op_lt(right),
|
|
BinaryOperator::Le => left.op_le(right),
|
|
BinaryOperator::Gt => left.op_gt(right),
|
|
BinaryOperator::Ge => left.op_ge(right),
|
|
});
|
|
}
|
|
ExpressionItem::Function { id, num_args } => {
|
|
let num_args = *num_args as usize;
|
|
|
|
let mut arguments = Variable::array(num_args);
|
|
for arg_num in 0..num_args {
|
|
arguments[num_args - arg_num - 1] = stack.pop().unwrap_or_default();
|
|
}
|
|
|
|
let result = if let Some((_, fnc, _)) = FUNCTIONS.get(*id as usize) {
|
|
(fnc)(arguments)
|
|
} else {
|
|
Box::pin(self.core.eval_fnc(
|
|
*id - FUNCTIONS.len() as u32,
|
|
arguments,
|
|
self.session_id,
|
|
))
|
|
.await?
|
|
};
|
|
|
|
stack.push(result);
|
|
}
|
|
ExpressionItem::JmpIf { val, pos } => {
|
|
if stack.last().is_some_and(|v| v.to_bool()) == *val {
|
|
for _ in 0..*pos {
|
|
exprs.next();
|
|
}
|
|
}
|
|
}
|
|
ExpressionItem::ArrayAccess => {
|
|
let index = stack
|
|
.pop()
|
|
.unwrap_or_default()
|
|
.to_usize()
|
|
.unwrap_or_default();
|
|
let array = stack.pop().unwrap_or_default().into_array();
|
|
stack.push(array.into_iter().nth(index).unwrap_or_default());
|
|
}
|
|
ExpressionItem::ArrayBuild(num_items) => {
|
|
let num_items = *num_items as usize;
|
|
let mut items = Variable::array(num_items);
|
|
for arg_num in 0..num_items {
|
|
items[num_items - arg_num - 1] = stack.pop().unwrap_or_default();
|
|
}
|
|
stack.push(Variable::Array(items));
|
|
}
|
|
ExpressionItem::Regex(regex) => {
|
|
self.captures.clear();
|
|
let value = stack.pop().unwrap_or_default().into_string();
|
|
|
|
if let Some(captures_) = regex.captures(value.as_ref()) {
|
|
for capture in captures_.iter() {
|
|
self.captures
|
|
.push(capture.map_or("", |m| m.as_str()).to_compact_string());
|
|
}
|
|
}
|
|
|
|
stack.push(Variable::Integer(!self.captures.is_empty() as i64));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(stack.pop().unwrap_or_default())
|
|
}
|
|
}
|
|
|
|
impl Expression {
|
|
pub fn is_empty(&self) -> bool {
|
|
self.items.is_empty()
|
|
}
|
|
|
|
pub fn items(&self) -> &[ExpressionItem] {
|
|
&self.items
|
|
}
|
|
}
|
|
|
|
impl<'x> Variable<'x> {
|
|
pub fn op_add(self, other: Variable<'x>) -> Variable<'x> {
|
|
match (self, other) {
|
|
(Variable::Integer(a), Variable::Integer(b)) => Variable::Integer(a.saturating_add(b)),
|
|
(Variable::Float(a), Variable::Float(b)) => Variable::Float(a + b),
|
|
(Variable::Integer(i), Variable::Float(f))
|
|
| (Variable::Float(f), Variable::Integer(i)) => Variable::Float(i as f64 + f),
|
|
(Variable::Array(a), Variable::Array(b)) => {
|
|
Variable::Array(a.into_iter().chain(b).collect::<Vec<_>>())
|
|
}
|
|
(Variable::Array(a), b) => {
|
|
Variable::Array(a.into_iter().chain([b]).collect::<Vec<_>>())
|
|
}
|
|
(a, Variable::Array(b)) => {
|
|
Variable::Array([a].into_iter().chain(b).collect::<Vec<_>>())
|
|
}
|
|
(Variable::String(a), b) => {
|
|
if !a.is_empty() {
|
|
Variable::String(StringCow::Owned(format_compact!("{}{}", a, b)))
|
|
} else {
|
|
b
|
|
}
|
|
}
|
|
(a, Variable::String(b)) => {
|
|
if !b.is_empty() {
|
|
Variable::String(StringCow::Owned(format_compact!("{}{}", a, b)))
|
|
} else {
|
|
a
|
|
}
|
|
}
|
|
(a, Variable::Constant(_)) => a,
|
|
(Variable::Constant(_), b) => b,
|
|
}
|
|
}
|
|
|
|
pub fn op_subtract(self, other: Variable<'x>) -> Variable<'x> {
|
|
match (self, other) {
|
|
(Variable::Integer(a), Variable::Integer(b)) => Variable::Integer(a.saturating_sub(b)),
|
|
(Variable::Float(a), Variable::Float(b)) => Variable::Float(a - b),
|
|
(Variable::Integer(a), Variable::Float(b)) => Variable::Float(a as f64 - b),
|
|
(Variable::Float(a), Variable::Integer(b)) => Variable::Float(a - b as f64),
|
|
(Variable::Array(a), b) | (b, Variable::Array(a)) => {
|
|
Variable::Array(a.into_iter().filter(|v| v != &b).collect::<Vec<_>>())
|
|
}
|
|
(a, b) => a.parse_number().op_subtract(b.parse_number()),
|
|
}
|
|
}
|
|
|
|
pub fn op_multiply(self, other: Variable<'x>) -> Variable<'x> {
|
|
match (self, other) {
|
|
(Variable::Integer(a), Variable::Integer(b)) => Variable::Integer(a.saturating_mul(b)),
|
|
(Variable::Float(a), Variable::Float(b)) => Variable::Float(a * b),
|
|
(Variable::Integer(i), Variable::Float(f))
|
|
| (Variable::Float(f), Variable::Integer(i)) => Variable::Float(i as f64 * f),
|
|
(a, b) => a.parse_number().op_multiply(b.parse_number()),
|
|
}
|
|
}
|
|
|
|
pub fn op_divide(self, other: Variable<'x>) -> Variable<'x> {
|
|
match (self, other) {
|
|
(Variable::Integer(a), Variable::Integer(b)) => {
|
|
Variable::Float(if b != 0 { a as f64 / b as f64 } else { 0.0 })
|
|
}
|
|
(Variable::Float(a), Variable::Float(b)) => {
|
|
Variable::Float(if b != 0.0 { a / b } else { 0.0 })
|
|
}
|
|
(Variable::Integer(a), Variable::Float(b)) => {
|
|
Variable::Float(if b != 0.0 { a as f64 / b } else { 0.0 })
|
|
}
|
|
(Variable::Float(a), Variable::Integer(b)) => {
|
|
Variable::Float(if b != 0 { a / b as f64 } else { 0.0 })
|
|
}
|
|
(a, b) => a.parse_number().op_divide(b.parse_number()),
|
|
}
|
|
}
|
|
|
|
pub fn op_and(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self.to_bool() & other.to_bool()))
|
|
}
|
|
|
|
pub fn op_or(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self.to_bool() | other.to_bool()))
|
|
}
|
|
|
|
pub fn op_xor(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self.to_bool() ^ other.to_bool()))
|
|
}
|
|
|
|
pub fn op_eq(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self == other))
|
|
}
|
|
|
|
pub fn op_ne(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self != other))
|
|
}
|
|
|
|
pub fn op_lt(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self < other))
|
|
}
|
|
|
|
pub fn op_le(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self <= other))
|
|
}
|
|
|
|
pub fn op_gt(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self > other))
|
|
}
|
|
|
|
pub fn op_ge(self, other: Variable) -> Variable {
|
|
Variable::Integer(i64::from(self >= other))
|
|
}
|
|
|
|
pub fn op_not(self) -> Variable<'static> {
|
|
Variable::Integer(i64::from(!self.to_bool()))
|
|
}
|
|
|
|
pub fn op_minus(self) -> Variable<'static> {
|
|
match self {
|
|
Variable::Integer(n) => Variable::Integer(-n),
|
|
Variable::Float(n) => Variable::Float(-n),
|
|
_ => self.parse_number().op_minus(),
|
|
}
|
|
}
|
|
|
|
pub fn parse_number(&self) -> Variable<'static> {
|
|
match self {
|
|
Variable::String(s) if !s.is_empty() => {
|
|
if let Ok(n) = s.as_str().parse::<i64>() {
|
|
Variable::Integer(n)
|
|
} else if let Ok(n) = s.as_str().parse::<f64>() {
|
|
Variable::Float(n)
|
|
} else {
|
|
Variable::Integer(0)
|
|
}
|
|
}
|
|
Variable::Integer(n) => Variable::Integer(*n),
|
|
Variable::Float(n) => Variable::Float(*n),
|
|
Variable::Array(l) => Variable::Integer(l.is_empty() as i64),
|
|
_ => Variable::Integer(0),
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn array(num_items: usize) -> Vec<Variable<'static>> {
|
|
let mut items = Vec::with_capacity(num_items);
|
|
for _ in 0..num_items {
|
|
items.push(Variable::Integer(0));
|
|
}
|
|
items
|
|
}
|
|
|
|
pub fn to_ref<'y: 'x>(&'y self) -> Variable<'x> {
|
|
match self {
|
|
Variable::String(s) => Variable::String(StringCow::Borrowed(s.as_str())),
|
|
Variable::Integer(n) => Variable::Integer(*n),
|
|
Variable::Float(n) => Variable::Float(*n),
|
|
Variable::Constant(c) => Variable::Constant(*c),
|
|
Variable::Array(l) => Variable::Array(l.iter().map(|v| v.to_ref()).collect::<Vec<_>>()),
|
|
}
|
|
}
|
|
|
|
pub fn to_bool(&self) -> bool {
|
|
match self {
|
|
Variable::Float(f) => *f != 0.0,
|
|
Variable::Integer(n) => *n != 0,
|
|
Variable::String(s) => !s.is_empty(),
|
|
Variable::Array(a) => !a.is_empty(),
|
|
Variable::Constant(_) => true,
|
|
}
|
|
}
|
|
|
|
pub fn to_string(&'_ self) -> StringCow<'_> {
|
|
match self {
|
|
Variable::String(s) => StringCow::Borrowed(s.as_str()),
|
|
Variable::Integer(n) => StringCow::Owned(n.to_compact_string()),
|
|
Variable::Float(n) => StringCow::Owned(n.to_compact_string()),
|
|
Variable::Array(l) => {
|
|
let mut result = CompactString::with_capacity(self.len() * 10);
|
|
for item in l {
|
|
if !result.is_empty() {
|
|
result.push_str("\r\n");
|
|
}
|
|
match item {
|
|
Variable::String(v) => result.push_str(v.as_str()),
|
|
Variable::Integer(v) => result.push_str(&v.to_compact_string()),
|
|
Variable::Float(v) => result.push_str(&v.to_compact_string()),
|
|
Variable::Array(_) => {}
|
|
Variable::Constant(c) => result.push_str(c.as_str()),
|
|
}
|
|
}
|
|
StringCow::Owned(result)
|
|
}
|
|
Variable::Constant(c) => StringCow::Borrowed(c.as_str()),
|
|
}
|
|
}
|
|
|
|
pub fn into_string(self) -> StringCow<'x> {
|
|
match self {
|
|
Variable::String(s) => s,
|
|
Variable::Integer(n) => StringCow::Owned(n.to_compact_string()),
|
|
Variable::Float(n) => StringCow::Owned(n.to_compact_string()),
|
|
Variable::Array(l) => {
|
|
let mut result = CompactString::with_capacity(l.len() * 10);
|
|
for item in l {
|
|
if !result.is_empty() {
|
|
result.push_str("\r\n");
|
|
}
|
|
match item {
|
|
Variable::String(v) => result.push_str(v.as_ref()),
|
|
Variable::Integer(v) => result.push_str(&v.to_compact_string()),
|
|
Variable::Float(v) => result.push_str(&v.to_compact_string()),
|
|
Variable::Array(_) => {}
|
|
Variable::Constant(c) => result.push_str(c.as_str()),
|
|
}
|
|
}
|
|
StringCow::Owned(result)
|
|
}
|
|
Variable::Constant(c) => StringCow::Borrowed(c.as_str()),
|
|
}
|
|
}
|
|
|
|
pub fn to_integer(&self) -> Option<i64> {
|
|
match self {
|
|
Variable::Integer(n) => Some(*n),
|
|
Variable::Float(n) => Some(*n as i64),
|
|
Variable::String(s) if !s.is_empty() => s.as_str().parse::<i64>().ok(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn to_usize(&self) -> Option<usize> {
|
|
match self {
|
|
Variable::Integer(n) => Some(*n as usize),
|
|
Variable::Float(n) => Some(*n as usize),
|
|
Variable::String(s) if !s.is_empty() => s.as_str().parse::<usize>().ok(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
Variable::String(s) => s.len(),
|
|
Variable::Integer(_) | Variable::Float(_) => 2,
|
|
Variable::Array(l) => l.iter().map(|v| v.len() + 2).sum(),
|
|
Variable::Constant(c) => c.as_str().len(),
|
|
}
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
match self {
|
|
Variable::String(s) => s.is_empty(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn as_array(&'_ self) -> Option<&'_ [Variable<'_>]> {
|
|
match self {
|
|
Variable::Array(l) => Some(l),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn into_array(self) -> Vec<Variable<'x>> {
|
|
match self {
|
|
Variable::Array(l) => l,
|
|
v if !v.is_empty() => vec![v],
|
|
_ => vec![],
|
|
}
|
|
}
|
|
|
|
pub fn to_array(&self) -> Vec<Variable<'_>> {
|
|
match self {
|
|
Variable::Array(l) => l.iter().map(|v| v.to_ref()).collect::<Vec<_>>(),
|
|
v if !v.is_empty() => vec![v.to_ref()],
|
|
_ => vec![],
|
|
}
|
|
}
|
|
|
|
pub fn into_owned(self) -> Variable<'static> {
|
|
match self {
|
|
Variable::String(s) => Variable::String(StringCow::Owned(s.into_owned())),
|
|
Variable::Integer(n) => Variable::Integer(n),
|
|
Variable::Float(n) => Variable::Float(n),
|
|
Variable::Constant(c) => Variable::Constant(c),
|
|
Variable::Array(l) => Variable::Array(l.into_iter().map(|v| v.into_owned()).collect()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Variable<'_> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
match (self, other) {
|
|
(Self::Integer(a), Self::Integer(b)) => a == b,
|
|
(Self::Float(a), Self::Float(b)) => a == b,
|
|
(Self::Integer(a), Self::Float(b)) | (Self::Float(b), Self::Integer(a)) => {
|
|
*a as f64 == *b
|
|
}
|
|
(Self::String(a), Self::String(b)) => a.as_str() == b.as_str(),
|
|
(Self::String(_), Self::Integer(_) | Self::Float(_)) => &self.parse_number() == other,
|
|
(Self::Integer(_) | Self::Float(_), Self::String(_)) => self == &other.parse_number(),
|
|
(Self::Array(a), Self::Array(b)) => a == b,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Eq for Variable<'_> {}
|
|
|
|
#[allow(clippy::non_canonical_partial_ord_impl)]
|
|
impl PartialOrd for Variable<'_> {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
match (self, other) {
|
|
(Self::Integer(a), Self::Integer(b)) => a.partial_cmp(b),
|
|
(Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
|
|
(Self::Integer(a), Self::Float(b)) => (*a as f64).partial_cmp(b),
|
|
(Self::Float(a), Self::Integer(b)) => a.partial_cmp(&(*b as f64)),
|
|
(Self::String(a), Self::String(b)) => a.as_str().partial_cmp(b.as_str()),
|
|
(Self::String(_), Self::Integer(_) | Self::Float(_)) => {
|
|
self.parse_number().partial_cmp(other)
|
|
}
|
|
(Self::Integer(_) | Self::Float(_), Self::String(_)) => {
|
|
self.partial_cmp(&other.parse_number())
|
|
}
|
|
(Self::Array(a), Self::Array(b)) => a.partial_cmp(b),
|
|
(Self::Array(_) | Self::String(_), _) => Ordering::Greater.into(),
|
|
(Self::Constant(a), Self::Constant(b)) => a.to_id().partial_cmp(&b.to_id()),
|
|
(_, Self::Array(_) | Self::Constant(_)) | (Self::Constant(_), _) => {
|
|
Ordering::Less.into()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Ord for Variable<'_> {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.partial_cmp(other).unwrap_or(Ordering::Greater)
|
|
}
|
|
}
|
|
|
|
impl Display for Variable<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Variable::String(v) => v.fmt(f),
|
|
Variable::Integer(v) => v.fmt(f),
|
|
Variable::Float(v) => v.fmt(f),
|
|
Variable::Array(v) => {
|
|
for (i, v) in v.iter().enumerate() {
|
|
if i > 0 {
|
|
f.write_str("\n")?;
|
|
}
|
|
v.fmt(f)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
Variable::Constant(c) => c.as_str().fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'x> From<&'x Constant> for Variable<'x> {
|
|
fn from(value: &'x Constant) -> Self {
|
|
match value {
|
|
Constant::Integer(i) => Variable::Integer(*i),
|
|
Constant::Float(f) => Variable::Float(*f),
|
|
Constant::String(s) => Variable::String(StringCow::Borrowed(s.as_str())),
|
|
Constant::Static(c) => Variable::Constant(*c),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for CompactString {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
if let Variable::String(s) = value {
|
|
Ok(match s {
|
|
StringCow::Borrowed(v) => v.into(),
|
|
StringCow::Owned(v) => v,
|
|
})
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for String {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
if let Variable::String(s) = value {
|
|
Ok(match s {
|
|
StringCow::Borrowed(v) => v.to_string(),
|
|
StringCow::Owned(v) => v.into_string(),
|
|
})
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'x> From<Variable<'x>> for bool {
|
|
fn from(val: Variable<'x>) -> Self {
|
|
val.to_bool()
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for i64 {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
value.to_integer().ok_or(())
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for u64 {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
value.to_integer().map(|v| v as u64).ok_or(())
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for usize {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
value.to_usize().ok_or(())
|
|
}
|
|
}
|
|
|
|
impl<'x> TryFrom<Variable<'x>> for StatusCode {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
|
|
match value.to_integer() {
|
|
Some(v) => match StatusCode::from_u16(v as u16) {
|
|
Ok(status) => Ok(status),
|
|
Err(_) => Err(()),
|
|
},
|
|
None => Err(()),
|
|
}
|
|
}
|
|
}
|