surrealpatch/lib/src/sql/constant.rs
2023-08-29 11:50:00 +01:00

192 lines
5.8 KiB
Rust

use crate::ctx::Context;
use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::error::IResult;
use crate::sql::value::Value;
use crate::sql::Datetime;
use chrono::TimeZone;
use chrono::Utc;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::value;
use nom::sequence::preceded;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Constant";
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[serde(rename = "$surrealdb::private::sql::Constant")]
#[revisioned(revision = 1)]
pub enum Constant {
MathE,
MathFrac1Pi,
MathFrac1Sqrt2,
MathFrac2Pi,
MathFrac2SqrtPi,
MathFracPi2,
MathFracPi3,
MathFracPi4,
MathFracPi6,
MathFracPi8,
MathInf,
MathLn10,
MathLn2,
MathLog102,
MathLog10E,
MathLog210,
MathLog2E,
MathPi,
MathSqrt2,
MathTau,
TimeEpoch,
// Add new variants here
}
/// A type of constant that may be converted to a value or JSON.
pub(crate) enum ConstantValue {
Float(f64),
Datetime(Datetime),
}
impl Constant {
pub(crate) fn value(&self) -> ConstantValue {
use std::f64::consts as f64c;
match self {
Self::MathE => ConstantValue::Float(f64c::E),
Self::MathFrac1Pi => ConstantValue::Float(f64c::FRAC_1_PI),
Self::MathFrac1Sqrt2 => ConstantValue::Float(f64c::FRAC_1_SQRT_2),
Self::MathFrac2Pi => ConstantValue::Float(f64c::FRAC_2_PI),
Self::MathFrac2SqrtPi => ConstantValue::Float(f64c::FRAC_2_SQRT_PI),
Self::MathFracPi2 => ConstantValue::Float(f64c::FRAC_PI_2),
Self::MathFracPi3 => ConstantValue::Float(f64c::FRAC_PI_3),
Self::MathFracPi4 => ConstantValue::Float(f64c::FRAC_PI_4),
Self::MathFracPi6 => ConstantValue::Float(f64c::FRAC_PI_6),
Self::MathFracPi8 => ConstantValue::Float(f64c::FRAC_PI_8),
Self::MathInf => ConstantValue::Float(f64::INFINITY),
Self::MathLn10 => ConstantValue::Float(f64c::LN_10),
Self::MathLn2 => ConstantValue::Float(f64c::LN_2),
Self::MathLog102 => ConstantValue::Float(f64c::LOG10_2),
Self::MathLog10E => ConstantValue::Float(f64c::LOG10_E),
Self::MathLog210 => ConstantValue::Float(f64c::LOG2_10),
Self::MathLog2E => ConstantValue::Float(f64c::LOG2_E),
Self::MathPi => ConstantValue::Float(f64c::PI),
Self::MathSqrt2 => ConstantValue::Float(f64c::SQRT_2),
Self::MathTau => ConstantValue::Float(f64c::TAU),
Self::TimeEpoch => ConstantValue::Datetime(Datetime(Utc.timestamp_nanos(0))),
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
_opt: &Options,
_txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
Ok(match self.value() {
ConstantValue::Datetime(d) => d.into(),
ConstantValue::Float(f) => f.into(),
})
}
}
impl fmt::Display for Constant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Self::MathE => "math::E",
Self::MathFrac1Pi => "math::FRAC_1_PI",
Self::MathFrac1Sqrt2 => "math::FRAC_1_SQRT_2",
Self::MathFrac2Pi => "math::FRAC_2_PI",
Self::MathFrac2SqrtPi => "math::FRAC_2_SQRT_PI",
Self::MathFracPi2 => "math::FRAC_PI_2",
Self::MathFracPi3 => "math::FRAC_PI_3",
Self::MathFracPi4 => "math::FRAC_PI_4",
Self::MathFracPi6 => "math::FRAC_PI_6",
Self::MathFracPi8 => "math::FRAC_PI_8",
Self::MathInf => "math::INF",
Self::MathLn10 => "math::LN_10",
Self::MathLn2 => "math::LN_2",
Self::MathLog102 => "math::LOG10_2",
Self::MathLog10E => "math::LOG10_E",
Self::MathLog210 => "math::LOG2_10",
Self::MathLog2E => "math::LOG2_E",
Self::MathPi => "math::PI",
Self::MathSqrt2 => "math::SQRT_2",
Self::MathTau => "math::TAU",
Self::TimeEpoch => "time::EPOCH",
})
}
}
pub fn constant(i: &str) -> IResult<&str, Constant> {
alt((constant_math, constant_time))(i)
}
fn constant_math(i: &str) -> IResult<&str, Constant> {
preceded(
tag_no_case("math::"),
alt((
value(Constant::MathE, tag_no_case("E")),
value(Constant::MathFrac1Pi, tag_no_case("FRAC_1_PI")),
value(Constant::MathFrac1Sqrt2, tag_no_case("FRAC_1_SQRT_2")),
value(Constant::MathFrac2Pi, tag_no_case("FRAC_2_PI")),
value(Constant::MathFrac2SqrtPi, tag_no_case("FRAC_2_SQRT_PI")),
value(Constant::MathFracPi2, tag_no_case("FRAC_PI_2")),
value(Constant::MathFracPi3, tag_no_case("FRAC_PI_3")),
value(Constant::MathFracPi4, tag_no_case("FRAC_PI_4")),
value(Constant::MathFracPi6, tag_no_case("FRAC_PI_6")),
value(Constant::MathFracPi8, tag_no_case("FRAC_PI_8")),
value(Constant::MathInf, tag_no_case("INF")),
value(Constant::MathLn10, tag_no_case("LN_10")),
value(Constant::MathLn2, tag_no_case("LN_2")),
value(Constant::MathLog102, tag_no_case("LOG10_2")),
value(Constant::MathLog10E, tag_no_case("LOG10_E")),
value(Constant::MathLog210, tag_no_case("LOG2_10")),
value(Constant::MathLog2E, tag_no_case("LOG2_E")),
value(Constant::MathPi, tag_no_case("PI")),
value(Constant::MathSqrt2, tag_no_case("SQRT_2")),
value(Constant::MathTau, tag_no_case("TAU")),
)),
)(i)
}
fn constant_time(i: &str) -> IResult<&str, Constant> {
preceded(tag_no_case("time::"), alt((value(Constant::TimeEpoch, tag_no_case("EPOCH")),)))(i)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constant_lowercase() {
let sql = "math::pi";
let res = constant(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(out, Constant::MathPi);
}
#[test]
fn constant_uppercase() {
let sql = "MATH::PI";
let res = constant(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(out, Constant::MathPi);
}
#[test]
fn constant_mixedcase() {
let sql = "math::PI";
let res = constant(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(out, Constant::MathPi);
}
}