use std::cmp::Ordering;
use std::convert::{TryFrom, TryInto};
use sqlparser::ast::Value as AstValue;
use crate::data;
use crate::data::Value;
use crate::result::{Error, Result};
use super::EvaluateError;
#[derive(std::fmt::Debug)]
pub enum Evaluated<'a> {
LiteralRef(&'a AstValue),
Literal(AstValue),
StringRef(&'a str),
ValueRef(&'a Value),
Value(Value),
}
impl<'a> PartialEq for Evaluated<'a> {
fn eq(&self, other: &Evaluated<'a>) -> bool {
let eq_ast = |l: &AstValue, r| match l {
AstValue::SingleQuotedString(l) => l == r,
_ => false,
};
let eq_val = |l: &Value, r| match l {
Value::Str(l) => l == r,
_ => false,
};
{
use Evaluated::*;
match self {
LiteralRef(l) => match other {
LiteralRef(r) => l == r,
StringRef(r) => eq_ast(l, r),
ValueRef(r) => r == l,
Value(r) => &r == l,
Literal(r) => *l == r,
},
StringRef(l) => match other {
LiteralRef(r) => eq_ast(r, l),
StringRef(r) => l == r,
ValueRef(r) => eq_val(r, l),
Value(r) => eq_val(&r, l),
Literal(_) => false,
},
ValueRef(l) => match other {
LiteralRef(r) => l == r,
Literal(r) => l == &r,
StringRef(r) => eq_val(l, r),
ValueRef(r) => l == r,
Value(r) => l == &r,
},
Value(l) => match other {
LiteralRef(r) => &l == r,
StringRef(r) => eq_val(&l, r),
ValueRef(r) => &l == r,
Value(r) => l == r,
Literal(r) => l == r,
},
Literal(l) => match other {
Literal(r) => l == r,
LiteralRef(r) => &l == r,
ValueRef(r) => r == &l,
Value(r) => r == l,
StringRef(_) => false,
},
}
}
}
}
impl<'a> PartialOrd for Evaluated<'a> {
fn partial_cmp(&self, other: &Evaluated<'a>) -> Option<Ordering> {
use Evaluated::*;
match self {
LiteralRef(l) => match other {
LiteralRef(r) => literal_partial_cmp(l, r),
ValueRef(r) => r.partial_cmp(l).map(|o| o.reverse()),
Value(r) => r.partial_cmp(*l).map(|o| o.reverse()),
StringRef(_) => None,
Literal(r) => literal_partial_cmp(&l, &r),
},
ValueRef(l) => match other {
LiteralRef(r) => l.partial_cmp(r),
ValueRef(r) => l.partial_cmp(r),
Value(r) => l.partial_cmp(&r),
StringRef(r) => match l {
data::Value::Str(l) => (&l.as_str()).partial_cmp(r),
_ => None,
},
Literal(r) => l.partial_cmp(&r),
},
Value(l) => match other {
LiteralRef(r) => l.partial_cmp(*r),
ValueRef(r) => l.partial_cmp(*r),
Value(r) => l.partial_cmp(r),
StringRef(r) => match l {
data::Value::Str(l) => (&l.as_str()).partial_cmp(r),
_ => None,
},
Literal(r) => l.partial_cmp(r),
},
StringRef(l) => match other {
LiteralRef(_) => None,
ValueRef(data::Value::Str(r)) => l.partial_cmp(&r.as_str()),
Value(data::Value::Str(r)) => l.partial_cmp(&r.as_str()),
StringRef(r) => l.partial_cmp(r),
Literal(_) => panic!(),
_ => None,
},
Literal(l) => match other {
LiteralRef(r) => literal_partial_cmp(&l, &r),
ValueRef(r) => r.partial_cmp(&l).map(|o| o.reverse()),
Value(r) => r.partial_cmp(l).map(|o| o.reverse()),
StringRef(_) => None,
Literal(r) => literal_partial_cmp(l, r),
},
}
}
}
fn literal_partial_cmp(a: &AstValue, b: &AstValue) -> Option<Ordering> {
match (a, b) {
(AstValue::Number(a), AstValue::Number(b)) => match (a.parse::<i64>(), b.parse::<i64>()) {
(Ok(a), Ok(b)) => Some(a.cmp(&b)),
(_, Ok(b)) => match a.parse::<f64>() {
Ok(a) => a.partial_cmp(&(b as f64)),
_ => None,
},
(Ok(a), _) => match b.parse::<f64>() {
Ok(b) => (a as f64).partial_cmp(&b),
_ => None,
},
_ => match (a.parse::<f64>(), b.parse::<f64>()) {
(Ok(a), Ok(b)) => a.partial_cmp(&b),
_ => None,
},
},
(AstValue::SingleQuotedString(l), AstValue::SingleQuotedString(r)) => Some(l.cmp(r)),
_ => None,
}
}
impl TryInto<Value> for Evaluated<'_> {
type Error = Error;
fn try_into(self) -> Result<Value> {
match self {
Evaluated::LiteralRef(v) => Value::try_from(v),
Evaluated::Literal(v) => Value::try_from(&v),
Evaluated::StringRef(v) => Ok(Value::Str(v.to_string())),
Evaluated::ValueRef(v) => Ok(v.clone()),
Evaluated::Value(v) => Ok(v),
}
}
}
impl<'a> Evaluated<'a> {
pub fn add(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let add_literal = |l, other: &Evaluated<'a>| match other {
LiteralRef(r) => literal_add(l, r).map(Evaluated::Literal),
Literal(r) => literal_add(l, &r).map(Evaluated::Literal),
ValueRef(r) => r.add(&r.clone_by(l)?).map(Evaluated::Value),
Value(r) => r.add(&r.clone_by(l)?).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
let add_value = |l: &data::Value, other: &Evaluated<'a>| match other {
LiteralRef(r) => l.add(&l.clone_by(r)?).map(Evaluated::Value),
Literal(r) => l.add(&l.clone_by(&r)?).map(Evaluated::Value),
ValueRef(r) => l.add(r).map(Evaluated::Value),
Value(r) => l.add(&r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
match self {
LiteralRef(l) => add_literal(l, other),
Literal(l) => add_literal(&l, other),
ValueRef(l) => add_value(l, other),
Value(l) => add_value(&l, other),
StringRef(_) => unreachable(),
}
}
pub fn subtract(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let subtract_literal = |l, other: &Evaluated<'a>| match other {
LiteralRef(r) => literal_subtract(l, r).map(Evaluated::Literal),
Literal(r) => literal_subtract(l, &r).map(Evaluated::Literal),
ValueRef(r) => (r.clone_by(l)?).subtract(r).map(Evaluated::Value),
Value(r) => (r.clone_by(l)?).subtract(r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
let subtract_value = |l: &data::Value, other: &Evaluated<'a>| match other {
LiteralRef(r) => l.subtract(&l.clone_by(r)?).map(Evaluated::Value),
Literal(r) => l.subtract(&l.clone_by(&r)?).map(Evaluated::Value),
ValueRef(r) => l.subtract(r).map(Evaluated::Value),
Value(r) => l.subtract(&r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
match self {
LiteralRef(l) => subtract_literal(l, other),
Literal(l) => subtract_literal(&l, other),
ValueRef(l) => subtract_value(l, other),
Value(l) => subtract_value(&l, other),
StringRef(_) => unreachable(),
}
}
pub fn multiply(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let multiply_literal = |l, other: &Evaluated<'a>| match other {
LiteralRef(r) => literal_multiply(l, r).map(Evaluated::Literal),
Literal(r) => literal_multiply(l, &r).map(Evaluated::Literal),
ValueRef(r) => (r.clone_by(l)?).multiply(r).map(Evaluated::Value),
Value(r) => (r.clone_by(l)?).multiply(r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
let multiply_value = |l: &data::Value, other: &Evaluated<'a>| match other {
LiteralRef(r) => l.multiply(&l.clone_by(r)?).map(Evaluated::Value),
Literal(r) => l.multiply(&l.clone_by(&r)?).map(Evaluated::Value),
ValueRef(r) => l.multiply(r).map(Evaluated::Value),
Value(r) => l.multiply(&r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
match self {
LiteralRef(l) => multiply_literal(l, other),
Literal(l) => multiply_literal(&l, other),
ValueRef(l) => multiply_value(l, other),
Value(l) => multiply_value(&l, other),
StringRef(_) => unreachable(),
}
}
pub fn divide(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let divide_literal = |l, other: &Evaluated<'a>| match other {
LiteralRef(r) => literal_divide(l, r).map(Evaluated::Literal),
Literal(r) => literal_divide(l, &r).map(Evaluated::Literal),
ValueRef(r) => (r.clone_by(l)?).divide(r).map(Evaluated::Value),
Value(r) => (r.clone_by(l)?).divide(r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
let divide_value = |l: &data::Value, other: &Evaluated<'a>| match other {
LiteralRef(r) => l.divide(&l.clone_by(r)?).map(Evaluated::Value),
Literal(r) => l.divide(&l.clone_by(&r)?).map(Evaluated::Value),
ValueRef(r) => l.divide(r).map(Evaluated::Value),
Value(r) => l.divide(&r).map(Evaluated::Value),
StringRef(_) => unreachable(),
};
match self {
LiteralRef(l) => divide_literal(l, other),
Literal(l) => divide_literal(&l, other),
ValueRef(l) => divide_value(l, other),
Value(l) => divide_value(&l, other),
StringRef(_) => unreachable(),
}
}
pub fn is_some(&self) -> bool {
match self {
Evaluated::ValueRef(v) => v.is_some(),
Evaluated::Value(v) => v.is_some(),
Evaluated::Literal(v) => v != &AstValue::Null,
Evaluated::LiteralRef(v) => v != &&AstValue::Null,
Evaluated::StringRef(_v) => true,
}
}
pub fn unary_plus(&self) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let plus_literal = |v| literal_plus(v).map(Evaluated::Literal);
let plus_value = |v: &data::Value| v.unary_plus().map(Evaluated::Value);
match self {
LiteralRef(v) => plus_literal(v),
Literal(v) => plus_literal(&v),
ValueRef(v) => plus_value(v),
Value(v) => plus_value(&v),
StringRef(_) => unreachable(),
}
}
pub fn unary_minus(&self) -> Result<Evaluated<'a>> {
use Evaluated::*;
let unreachable = || Err(EvaluateError::UnreachableEvaluatedArithmetic.into());
let minus_literal = |v| literal_minus(v).map(Evaluated::Literal);
let minus_value = |v: &data::Value| v.unary_minus().map(Evaluated::Value);
match self {
LiteralRef(v) => minus_literal(v),
Literal(v) => minus_literal(&v),
ValueRef(v) => minus_value(v),
Value(v) => minus_value(&v),
StringRef(_) => unreachable(),
}
}
}
fn literal_add(a: &AstValue, b: &AstValue) -> Result<AstValue> {
match (a, b) {
(AstValue::Number(a), AstValue::Number(b)) => match (a.parse::<i64>(), b.parse::<i64>()) {
(Ok(a), Ok(b)) => Ok(AstValue::Number((a + b).to_string())),
_ => panic!(),
},
(AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
Ok(AstValue::Null)
}
_ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
}
}
fn literal_subtract(a: &AstValue, b: &AstValue) -> Result<AstValue> {
match (a, b) {
(AstValue::Number(a), AstValue::Number(b)) => match (a.parse::<i64>(), b.parse::<i64>()) {
(Ok(a), Ok(b)) => Ok(AstValue::Number((a - b).to_string())),
_ => panic!(),
},
(AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
Ok(AstValue::Null)
}
_ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
}
}
fn literal_multiply(a: &AstValue, b: &AstValue) -> Result<AstValue> {
match (a, b) {
(AstValue::Number(a), AstValue::Number(b)) => match (a.parse::<i64>(), b.parse::<i64>()) {
(Ok(a), Ok(b)) => Ok(AstValue::Number((a * b).to_string())),
_ => panic!(),
},
(AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
Ok(AstValue::Null)
}
_ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
}
}
fn literal_divide(a: &AstValue, b: &AstValue) -> Result<AstValue> {
match (a, b) {
(AstValue::Number(a), AstValue::Number(b)) => match (a.parse::<i64>(), b.parse::<i64>()) {
(Ok(a), Ok(b)) => Ok(AstValue::Number((a / b).to_string())),
_ => panic!(),
},
(AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
Ok(AstValue::Null)
}
_ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
}
}
fn literal_plus(v: &AstValue) -> Result<AstValue> {
match v {
AstValue::Number(v) => v
.parse::<i64>()
.map_or_else(
|_| v.parse::<f64>().map(|_| AstValue::Number(v.to_string())),
|v| Ok(AstValue::Number(v.to_string())),
)
.map_err(|_| EvaluateError::LiteralUnaryPlusOnNonNumeric.into()),
AstValue::Null => Ok(AstValue::Null),
_ => Err(EvaluateError::LiteralUnaryPlusOnNonNumeric.into()),
}
}
fn literal_minus(v: &AstValue) -> Result<AstValue> {
match v {
AstValue::Number(v) => v
.parse::<i64>()
.map_or_else(
|_| v.parse::<f64>().map(|v| AstValue::Number((-v).to_string())),
|v| Ok(AstValue::Number((-v).to_string())),
)
.map_err(|_| EvaluateError::LiteralUnaryMinusOnNonNumeric.into()),
AstValue::Null => Ok(AstValue::Null),
_ => Err(EvaluateError::LiteralUnaryMinusOnNonNumeric.into()),
}
}