#[cfg(test)]
mod tests;
use crate::model::field::FieldKind;
use crate::types::Decimal;
use crate::value::Value;
use std::cmp::Ordering;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum NumericArithmeticOp {
Add,
Sub,
Mul,
Div,
}
#[must_use]
pub(in crate::db) fn apply_decimal_arithmetic(
op: NumericArithmeticOp,
left: Decimal,
right: Decimal,
) -> Decimal {
match op {
NumericArithmeticOp::Add => left + right,
NumericArithmeticOp::Sub => left - right,
NumericArithmeticOp::Mul => left * right,
NumericArithmeticOp::Div => left / right,
}
}
#[must_use]
pub(in crate::db) fn add_decimal_terms(left: Decimal, right: Decimal) -> Decimal {
apply_decimal_arithmetic(NumericArithmeticOp::Add, left, right)
}
#[must_use]
pub(in crate::db) fn divide_decimal_terms(left: Decimal, right: Decimal) -> Decimal {
apply_decimal_arithmetic(NumericArithmeticOp::Div, left, right)
}
#[must_use]
pub(in crate::db) fn average_decimal_terms(sum: Decimal, count: u64) -> Option<Decimal> {
let divisor = Decimal::from_num(count)?;
Some(divide_decimal_terms(sum, divisor))
}
#[must_use]
pub(in crate::db) const fn field_kind_supports_expr_numeric(kind: &FieldKind) -> bool {
matches!(
kind,
FieldKind::Int
| FieldKind::Int128
| FieldKind::IntBig
| FieldKind::Uint
| FieldKind::Uint128
| FieldKind::UintBig
| FieldKind::Duration
| FieldKind::Timestamp
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Decimal { .. }
)
}
#[must_use]
pub(in crate::db) const fn field_kind_supports_aggregate_numeric(kind: &FieldKind) -> bool {
match kind {
FieldKind::Decimal { .. }
| FieldKind::Duration
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Int
| FieldKind::Int128
| FieldKind::IntBig
| FieldKind::Timestamp
| FieldKind::Uint
| FieldKind::Uint128
| FieldKind::UintBig => true,
FieldKind::Relation { key_kind, .. } => field_kind_supports_aggregate_numeric(key_kind),
FieldKind::Account
| FieldKind::Blob
| FieldKind::Bool
| FieldKind::Date
| FieldKind::Enum { .. }
| FieldKind::List(_)
| FieldKind::Map { .. }
| FieldKind::Principal
| FieldKind::Set(_)
| FieldKind::Structured { .. }
| FieldKind::Subaccount
| FieldKind::Text
| FieldKind::Ulid
| FieldKind::Unit => false,
}
}
#[must_use]
pub(in crate::db) fn coerce_numeric_decimal(value: &Value) -> Option<Decimal> {
if !value.supports_numeric_coercion() {
return None;
}
value.to_numeric_decimal()
}
#[must_use]
pub(in crate::db) fn apply_numeric_arithmetic(
op: NumericArithmeticOp,
left: &Value,
right: &Value,
) -> Option<Decimal> {
let left = coerce_numeric_decimal(left)?;
let right = coerce_numeric_decimal(right)?;
let result = apply_decimal_arithmetic(op, left, right);
Some(result)
}
#[must_use]
pub(in crate::db) fn compare_numeric_order(left: &Value, right: &Value) -> Option<Ordering> {
let left = coerce_numeric_decimal(left)?;
let right = coerce_numeric_decimal(right)?;
left.partial_cmp(&right)
}
#[must_use]
pub(in crate::db) fn compare_numeric_or_strict_order(
left: &Value,
right: &Value,
) -> Option<Ordering> {
compare_numeric_order(left, right).or_else(|| Value::strict_order_cmp(left, right))
}
#[must_use]
pub(in crate::db) fn compare_numeric_eq(left: &Value, right: &Value) -> Option<bool> {
compare_numeric_order(left, right).map(|ordering| ordering == Ordering::Equal)
}