ddb_core/functions/
conditional.rs1use super::Value;
3use crate::error::Result;
4
5pub 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
23pub 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
32pub 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
41pub 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
50pub 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
74pub 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
98pub 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}