use serde::{Deserialize, Serialize};
use crate::{ScalarKind, ScalarLiteral, SchemaContractError};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum SourceRuleOperation {
LengthRangeInclusive {
min: u64,
max: u64,
},
MultipleOf {
divisor: ScalarLiteral,
},
NumericMaximumInclusive {
value: ScalarLiteral,
},
NumericMinimumInclusive {
value: ScalarLiteral,
},
NumericRangeInclusive {
min: ScalarLiteral,
max: ScalarLiteral,
},
}
impl SourceRuleOperation {
pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
match self {
Self::LengthRangeInclusive { min, max } if min <= max => Ok(()),
Self::MultipleOf { divisor }
if exact_multiple_literal(divisor) && !scalar_literal_is_zero(divisor) =>
{
divisor.validate()
}
Self::NumericMaximumInclusive { value } | Self::NumericMinimumInclusive { value }
if numeric_literal(value) =>
{
value.validate()
}
Self::NumericRangeInclusive { min, max }
if numeric_literal(min)
&& min.kind() == max.kind()
&& scalar_literal_le(min, max) =>
{
min.validate()?;
max.validate()
}
Self::LengthRangeInclusive { .. }
| Self::MultipleOf { .. }
| Self::NumericMaximumInclusive { .. }
| Self::NumericMinimumInclusive { .. }
| Self::NumericRangeInclusive { .. } => Err(SchemaContractError::InvalidRuleOperation),
}
}
}
pub(crate) const fn exact_multiple_literal(literal: &ScalarLiteral) -> bool {
matches!(
literal.kind(),
ScalarKind::Decimal | ScalarKind::Int128 | ScalarKind::Nat128
)
}
const fn numeric_literal(literal: &ScalarLiteral) -> bool {
matches!(
literal.kind(),
ScalarKind::Decimal
| ScalarKind::Float32
| ScalarKind::Float64
| ScalarKind::Int128
| ScalarKind::Nat128
| ScalarKind::U256
)
}
fn scalar_literal_le(left: &ScalarLiteral, right: &ScalarLiteral) -> bool {
match (left, right) {
(ScalarLiteral::Decimal(left), ScalarLiteral::Decimal(right)) => left <= right,
(ScalarLiteral::Float32(left), ScalarLiteral::Float32(right)) => left <= right,
(ScalarLiteral::Float64(left), ScalarLiteral::Float64(right)) => left <= right,
(ScalarLiteral::Int(left), ScalarLiteral::Int(right)) => left <= right,
(ScalarLiteral::Nat(left), ScalarLiteral::Nat(right)) => left <= right,
(ScalarLiteral::U256(left), ScalarLiteral::U256(right)) => left <= right,
_ => false,
}
}
fn scalar_literal_is_zero(literal: &ScalarLiteral) -> bool {
match literal {
ScalarLiteral::Decimal(value) => value.is_zero(),
ScalarLiteral::Int(value) => *value == 0,
ScalarLiteral::Nat(value) => *value == 0,
ScalarLiteral::U256(value) => *value == crate::U256::ZERO,
ScalarLiteral::Account(_)
| ScalarLiteral::Blob(_)
| ScalarLiteral::Bool(_)
| ScalarLiteral::Date(_)
| ScalarLiteral::Duration(_)
| ScalarLiteral::EnumUnit { .. }
| ScalarLiteral::Float32(_)
| ScalarLiteral::Float64(_)
| ScalarLiteral::Principal(_)
| ScalarLiteral::Subaccount(_)
| ScalarLiteral::Text(_)
| ScalarLiteral::Timestamp(_)
| ScalarLiteral::Ulid(_)
| ScalarLiteral::Unit(_) => false,
}
}
#[cfg(test)]
mod tests;