use chrono::prelude::*;
use std::fmt;
#[derive(Debug, PartialEq, Clone)]
pub enum Operator {
GT,
LT,
GTE,
LTE,
EQ,
NE,
LIKE,
NLIKE,
AND,
OR,
}
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Operator::GT => ">",
Operator::GTE => ">=",
Operator::LT => "<",
Operator::LTE => "<=",
Operator::EQ => "=",
Operator::NE => "!=",
Operator::LIKE => "~",
Operator::NLIKE => "!~",
Operator::AND => "AND",
Operator::OR => "OR",
}
)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
Operator(Operator),
LP,
RP,
EOF,
Bool(bool),
Str(String),
Float(f64),
Date(DateTime<Local>),
Invalid(String),
}
impl From<String> for Token {
fn from(s: String) -> Token {
Token::from(s.as_ref())
}
}
impl From<char> for Token {
fn from(c: char) -> Token {
Token::from(c.to_string())
}
}
impl From<f64> for Token {
fn from(num: f64) -> Token {
Token::Float(num)
}
}
impl From<bool> for Token {
fn from(b: bool) -> Token {
Token::Bool(b)
}
}
impl<'a> From<&'a str> for Token {
fn from(s: &str) -> Token {
match s {
"(" => Token::LP,
")" => Token::RP,
">" => Token::Operator(Operator::GT),
">=" => Token::Operator(Operator::GTE),
"<" => Token::Operator(Operator::LT),
"<=" => Token::Operator(Operator::LTE),
"=" => Token::Operator(Operator::EQ),
"!=" => Token::Operator(Operator::NE),
"^=" => Token::Operator(Operator::NE),
"~" => Token::Operator(Operator::LIKE),
"^" => Token::Operator(Operator::LIKE),
"!~" => Token::Operator(Operator::NLIKE),
"^^" => Token::Operator(Operator::NLIKE),
"AND" | "and" => Token::Operator(Operator::AND),
"OR" | "or" => Token::Operator(Operator::OR),
"True" | "true" => Token::Bool(true),
"False" | "false" => Token::Bool(false),
"" => Token::EOF,
_ => {
if let Ok(num) = s.parse::<f64>() {
return Token::Float(num);
}
if let Ok(date) = Local.datetime_from_str(s, "%Y-%m-%d %r") {
return Token::Date(date);
}
Token::Str(s.to_string())
}
}
}
}
impl From<DateTime<Local>> for Token {
fn from(dte: DateTime<Local>) -> Token {
Token::Date(dte)
}
}
impl Into<String> for Token {
fn into(self) -> String {
match self {
Token::Str(s) => format!("(String, {})", s),
Token::Invalid(s) => format!("(Invalid, {})", s),
Token::Date(d) => format!("(Date, {})", d),
Token::Float(num) => format!("(Float, {})", num),
Token::Bool(b) => format!("(Bool, {})", b),
Token::Operator(Operator::GT) => "(GT, >)".to_string(),
Token::Operator(Operator::LT) => "(LT, <)".to_string(),
Token::Operator(Operator::GTE) => "(GTE, >=)".to_string(),
Token::Operator(Operator::LTE) => "(LTE, <=)".to_string(),
Token::Operator(Operator::EQ) => "(EQ, =)".to_string(),
Token::Operator(Operator::NE) => "(NE, !=)".to_string(),
Token::Operator(Operator::LIKE) => "(LIKE, ~)".to_string(),
Token::Operator(Operator::NLIKE) => "(NLIKE, !~)".to_string(),
Token::Operator(Operator::AND) => "(AND, AND)".to_string(),
Token::Operator(Operator::OR) => "(OR, OR)".to_string(),
Token::LP => "(LP, '(')".to_string(),
Token::RP => "(RP, ')')".to_string(),
Token::EOF => "(EOF, EOF)".to_string(),
}
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Token: {}", self.to_string())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
macro_rules! token_tests {
($($name:ident: $literal:expr, $expected:expr,)*) => {
$(
#[test]
fn $name() {
let token = Token::from($literal);
assert_eq!(token, $expected)
}
)*
}
}
token_tests! {
left_paren: "(", Token::LP,
right_paren: ")", Token::RP,
greater_than: ">", Token::Operator(Operator::GT),
greater_than_or_eq: ">=", Token::Operator(Operator::GTE),
less_than: "<", Token::Operator(Operator::LT),
less_than_or_eq: "<=", Token::Operator(Operator::LTE),
eq: "=", Token::Operator(Operator::EQ),
ne_bang: "!=", Token::Operator(Operator::NE),
ne_caret: "^=", Token::Operator(Operator::NE),
like_tilde: "~", Token::Operator(Operator::LIKE),
like_caret: "^", Token::Operator(Operator::LIKE),
nlike_bang_tilde: "!~", Token::Operator(Operator::NLIKE),
nlike_caret: "^^", Token::Operator(Operator::NLIKE),
and_upper: "AND", Token::Operator(Operator::AND),
and_lower: "and", Token::Operator(Operator::AND),
or_upper: "OR", Token::Operator(Operator::OR),
or_lower: "or", Token::Operator(Operator::OR),
true_upper: "True", Token::Bool(true),
true_lower: "true", Token::Bool(true),
false_upper: "False", Token::Bool(false),
false_lower: "false", Token::Bool(false),
num_dot: "1.0", Token::Float(1.0),
num_no_dot: "1", Token::Float(1.0),
eof: "", Token::EOF,
}
}