#[derive(Debug, Clone, PartialEq)]
pub enum JoinValue {
Null,
Boolean(bool),
Integer(i64),
Float(f64),
String(String),
}
impl JoinValue {
pub fn is_null(&self) -> bool {
matches!(self, JoinValue::Null)
}
pub fn to_hash_key(&self) -> String {
match self {
JoinValue::Null => "__NULL__".to_string(),
JoinValue::Boolean(b) => format!("b:{}", b),
JoinValue::Integer(i) => format!("i:{}", i),
JoinValue::Float(f) => format!("f:{:?}", f),
JoinValue::String(s) => format!("s:{}", s),
}
}
pub fn equals(&self, other: &JoinValue) -> Option<bool> {
if self.is_null() || other.is_null() {
return None; }
match (self, other) {
(JoinValue::Boolean(a), JoinValue::Boolean(b)) => Some(a == b),
(JoinValue::Integer(a), JoinValue::Integer(b)) => Some(a == b),
(JoinValue::Integer(a), JoinValue::Float(b)) => Some((*a as f64) == *b),
(JoinValue::Float(a), JoinValue::Integer(b)) => Some(*a == (*b as f64)),
(JoinValue::Float(a), JoinValue::Float(b)) => Some(a == b),
(JoinValue::String(a), JoinValue::String(b)) => Some(a == b),
_ => Some(false), }
}
pub fn compare(&self, other: &JoinValue) -> Option<std::cmp::Ordering> {
if self.is_null() || other.is_null() {
return None;
}
match (self, other) {
(JoinValue::Boolean(a), JoinValue::Boolean(b)) => Some(a.cmp(b)),
(JoinValue::Integer(a), JoinValue::Integer(b)) => Some(a.cmp(b)),
(JoinValue::Integer(a), JoinValue::Float(b)) => (*a as f64).partial_cmp(b),
(JoinValue::Float(a), JoinValue::Integer(b)) => a.partial_cmp(&(*b as f64)),
(JoinValue::Float(a), JoinValue::Float(b)) => a.partial_cmp(b),
(JoinValue::String(a), JoinValue::String(b)) => Some(a.cmp(b)),
_ => None, }
}
pub fn negate(&self) -> Option<JoinValue> {
match self {
JoinValue::Integer(i) => Some(JoinValue::Integer(-i)),
JoinValue::Float(f) => Some(JoinValue::Float(-f)),
_ => None,
}
}
pub fn not(&self) -> Option<JoinValue> {
match self {
JoinValue::Boolean(b) => Some(JoinValue::Boolean(!b)),
_ => None,
}
}
pub fn add(&self, other: &JoinValue) -> Option<JoinValue> {
match (self, other) {
(JoinValue::Integer(a), JoinValue::Integer(b)) => Some(JoinValue::Integer(a + b)),
(JoinValue::Integer(a), JoinValue::Float(b)) => Some(JoinValue::Float(*a as f64 + b)),
(JoinValue::Float(a), JoinValue::Integer(b)) => Some(JoinValue::Float(a + *b as f64)),
(JoinValue::Float(a), JoinValue::Float(b)) => Some(JoinValue::Float(a + b)),
(JoinValue::String(a), JoinValue::String(b)) => {
Some(JoinValue::String(format!("{}{}", a, b)))
}
_ => None,
}
}
pub fn subtract(&self, other: &JoinValue) -> Option<JoinValue> {
match (self, other) {
(JoinValue::Integer(a), JoinValue::Integer(b)) => Some(JoinValue::Integer(a - b)),
(JoinValue::Integer(a), JoinValue::Float(b)) => Some(JoinValue::Float(*a as f64 - b)),
(JoinValue::Float(a), JoinValue::Integer(b)) => Some(JoinValue::Float(a - *b as f64)),
(JoinValue::Float(a), JoinValue::Float(b)) => Some(JoinValue::Float(a - b)),
_ => None,
}
}
pub fn multiply(&self, other: &JoinValue) -> Option<JoinValue> {
match (self, other) {
(JoinValue::Integer(a), JoinValue::Integer(b)) => Some(JoinValue::Integer(a * b)),
(JoinValue::Integer(a), JoinValue::Float(b)) => Some(JoinValue::Float(*a as f64 * b)),
(JoinValue::Float(a), JoinValue::Integer(b)) => Some(JoinValue::Float(a * *b as f64)),
(JoinValue::Float(a), JoinValue::Float(b)) => Some(JoinValue::Float(a * b)),
_ => None,
}
}
pub fn divide(&self, other: &JoinValue) -> Option<JoinValue> {
match (self, other) {
(JoinValue::Integer(a), JoinValue::Integer(b)) if *b != 0 => {
Some(JoinValue::Integer(a / b))
}
(JoinValue::Integer(a), JoinValue::Float(b)) if *b != 0.0 => {
Some(JoinValue::Float(*a as f64 / b))
}
(JoinValue::Float(a), JoinValue::Integer(b)) if *b != 0 => {
Some(JoinValue::Float(a / *b as f64))
}
(JoinValue::Float(a), JoinValue::Float(b)) if *b != 0.0 => {
Some(JoinValue::Float(a / b))
}
_ => None,
}
}
pub fn modulo(&self, other: &JoinValue) -> Option<JoinValue> {
match (self, other) {
(JoinValue::Integer(a), JoinValue::Integer(b)) if *b != 0 => {
Some(JoinValue::Integer(a % b))
}
(JoinValue::Integer(a), JoinValue::Float(b)) if *b != 0.0 => {
Some(JoinValue::Float(*a as f64 % b))
}
(JoinValue::Float(a), JoinValue::Integer(b)) if *b != 0 => {
Some(JoinValue::Float(a % *b as f64))
}
(JoinValue::Float(a), JoinValue::Float(b)) if *b != 0.0 => {
Some(JoinValue::Float(a % b))
}
_ => None,
}
}
pub fn to_bool(&self) -> Option<bool> {
match self {
JoinValue::Boolean(b) => Some(*b),
JoinValue::Null => None,
_ => None,
}
}
pub fn matches_like(&self, pattern: &JoinValue) -> Option<bool> {
match (self, pattern) {
(JoinValue::String(s), JoinValue::String(p)) => Some(Self::like_match(s, p)),
_ => None,
}
}
pub fn like_match(text: &str, pattern: &str) -> bool {
let text_chars: Vec<char> = text.chars().collect();
let pattern_chars: Vec<char> = pattern.chars().collect();
Self::like_match_recursive(&text_chars, 0, &pattern_chars, 0)
}
fn like_match_recursive(text: &[char], ti: usize, pattern: &[char], pi: usize) -> bool {
if pi >= pattern.len() {
return ti >= text.len();
}
let pattern_char = pattern[pi];
match pattern_char {
'%' => {
for i in ti..=text.len() {
if Self::like_match_recursive(text, i, pattern, pi + 1) {
return true;
}
}
false
}
'_' => {
if ti < text.len() {
Self::like_match_recursive(text, ti + 1, pattern, pi + 1)
} else {
false
}
}
c => {
if ti < text.len() && text[ti].eq_ignore_ascii_case(&c) {
Self::like_match_recursive(text, ti + 1, pattern, pi + 1)
} else {
false
}
}
}
}
}