ddb_core/functions/
conditional.rs

1// Conditional functions
2use super::Value;
3use crate::error::Result;
4
5/// IF(condition, true_value, false_value)
6pub fn if_fn(condition: &Value, true_val: &Value, false_val: &Value) -> Result<Value> {
7    let cond = match condition {
8        Value::Boolean(b) => *b,
9        Value::Integer(i) => *i != 0,
10        Value::Float(f) => *f != 0.0,
11        Value::Null => false,
12        Value::String(s) => !s.is_empty(),
13        _ => true,
14    };
15
16    Ok(if cond {
17        true_val.clone()
18    } else {
19        false_val.clone()
20    })
21}
22
23/// IFNULL(expr, alt_value) - Return alt_value if expr is NULL
24pub fn ifnull(expr: &Value, alt_value: &Value) -> Result<Value> {
25    if expr.is_null() {
26        Ok(alt_value.clone())
27    } else {
28        Ok(expr.clone())
29    }
30}
31
32/// NULLIF(expr1, expr2) - Return NULL if expr1 == expr2, otherwise expr1
33pub fn nullif(expr1: &Value, expr2: &Value) -> Result<Value> {
34    if expr1 == expr2 {
35        Ok(Value::Null)
36    } else {
37        Ok(expr1.clone())
38    }
39}
40
41/// COALESCE(val1, val2, ...) - Return first non-NULL value
42pub fn coalesce(values: &[Value]) -> Result<Value> {
43    values
44        .iter()
45        .find(|v| !v.is_null())
46        .cloned()
47        .ok_or_else(|| crate::error::DdbError::FunctionError("All values are NULL".to_string()))
48}
49
50/// GREATEST(val1, val2, ...) - Return greatest value
51pub fn greatest(values: &[Value]) -> Result<Value> {
52    values
53        .iter()
54        .max_by(|a, b| {
55            match (a, b) {
56                (Value::Integer(x), Value::Integer(y)) => x.cmp(y),
57                (Value::Float(x), Value::Float(y)) => {
58                    x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
59                }
60                (Value::String(x), Value::String(y)) => x.cmp(y),
61                (Value::Integer(x), Value::Float(y)) => {
62                    (*x as f64).partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
63                }
64                (Value::Float(x), Value::Integer(y)) => {
65                    x.partial_cmp(&(*y as f64)).unwrap_or(std::cmp::Ordering::Equal)
66                }
67                _ => std::cmp::Ordering::Equal,
68            }
69        })
70        .cloned()
71        .ok_or_else(|| crate::error::DdbError::FunctionError("No values for GREATEST".to_string()))
72}
73
74/// LEAST(val1, val2, ...) - Return smallest value
75pub fn least(values: &[Value]) -> Result<Value> {
76    values
77        .iter()
78        .min_by(|a, b| {
79            match (a, b) {
80                (Value::Integer(x), Value::Integer(y)) => x.cmp(y),
81                (Value::Float(x), Value::Float(y)) => {
82                    x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
83                }
84                (Value::String(x), Value::String(y)) => x.cmp(y),
85                (Value::Integer(x), Value::Float(y)) => {
86                    (*x as f64).partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal)
87                }
88                (Value::Float(x), Value::Integer(y)) => {
89                    x.partial_cmp(&(*y as f64)).unwrap_or(std::cmp::Ordering::Equal)
90                }
91                _ => std::cmp::Ordering::Equal,
92            }
93        })
94        .cloned()
95        .ok_or_else(|| crate::error::DdbError::FunctionError("No values for LEAST".to_string()))
96}
97
98/// ISNULL(expr) - Check if value is NULL
99pub fn isnull(expr: &Value) -> Result<Value> {
100    Ok(Value::Boolean(expr.is_null()))
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_if() {
109        assert_eq!(
110            if_fn(&Value::Boolean(true), &Value::from("yes"), &Value::from("no")).unwrap(),
111            Value::from("yes")
112        );
113        assert_eq!(
114            if_fn(&Value::Boolean(false), &Value::from("yes"), &Value::from("no")).unwrap(),
115            Value::from("no")
116        );
117    }
118
119    #[test]
120    fn test_ifnull() {
121        assert_eq!(
122            ifnull(&Value::Null, &Value::from("default")).unwrap(),
123            Value::from("default")
124        );
125        assert_eq!(
126            ifnull(&Value::from("value"), &Value::from("default")).unwrap(),
127            Value::from("value")
128        );
129    }
130
131    #[test]
132    fn test_nullif() {
133        assert_eq!(
134            nullif(&Value::Integer(5), &Value::Integer(5)).unwrap(),
135            Value::Null
136        );
137        assert_eq!(
138            nullif(&Value::Integer(5), &Value::Integer(10)).unwrap(),
139            Value::Integer(5)
140        );
141    }
142
143    #[test]
144    fn test_coalesce() {
145        let values = vec![Value::Null, Value::Null, Value::from("first"), Value::from("second")];
146        assert_eq!(coalesce(&values).unwrap(), Value::from("first"));
147    }
148
149    #[test]
150    fn test_greatest_least() {
151        let values = vec![Value::Integer(1), Value::Integer(5), Value::Integer(3)];
152        assert_eq!(greatest(&values).unwrap(), Value::Integer(5));
153        assert_eq!(least(&values).unwrap(), Value::Integer(1));
154    }
155}