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 { 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); } }