use crate::ast::Operator;
use crate::parsing::span::Span;
use std::collections::HashMap;
use std::sync::OnceLock;
pub const OPERATORS: &[(&str, Operator)] = &[
("=between=", Operator::Between),
("=notnull=", Operator::NotNull),
("=ilike=", Operator::Ilike),
("=like=", Operator::Like),
("=null=", Operator::Null),
("=neq=", Operator::Neq),
("=out=", Operator::Out),
("=in=", Operator::In),
("=eq=", Operator::Eq),
("=le=", Operator::Lte),
("=ge=", Operator::Gte),
("=lt=", Operator::Lt),
("=gt=", Operator::Gt),
("<=", Operator::Lte),
(">=", Operator::Gte),
("!=", Operator::Neq),
("==", Operator::Eq),
("<", Operator::Lt),
(">", Operator::Gt),
];
static OPERATOR_MAP: OnceLock<HashMap<&'static str, Operator>> = OnceLock::new();
pub fn operator_map() -> &'static HashMap<&'static str, Operator> {
OPERATOR_MAP.get_or_init(|| OPERATORS.iter().map(|(k, v)| (*k, v.clone())).collect())
}
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
Null,
Bool(bool),
Integer(i64),
Float(f64),
Date(String),
DateTime(String),
QuotedStr(String),
Word(String),
Op(Operator),
LParen,
RParen,
Comma,
Semi,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Spanned {
pub token: Token,
pub span: Span,
}
impl Spanned {
pub fn new(token: Token, span: Span) -> Self {
Spanned { token, span }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn operators_longest_first() {
let mut prev_len = usize::MAX;
for (s, _) in OPERATORS {
assert!(
s.len() <= prev_len,
"operator {s:?} is longer than its predecessor — order is wrong"
);
prev_len = s.len();
}
}
#[test]
fn longest_operator_is_between() {
assert_eq!(OPERATORS[0].0, "=between=");
}
#[test]
fn single_char_operators_are_last() {
let last = OPERATORS.last().unwrap().0;
assert_eq!(
last.len(),
1,
"last operator should be single-char, got {last:?}"
);
}
#[test]
fn spanned_carries_span() {
let span = Span::new(0, 4);
let s = Spanned::new(Token::Word("name".into()), span);
assert_eq!(s.span, span);
assert_eq!(s.token, Token::Word("name".into()));
}
}