ddb_core/functions/
mod.rs

1// SQL function implementations
2pub mod aggregate;
3pub mod math;
4pub mod string;
5pub mod conversion;
6pub mod datetime;
7pub mod system;
8pub mod conditional;
9pub mod utility;
10pub mod registry;
11
12use crate::error::{DdbError, Result};
13use serde::{Deserialize, Serialize};
14
15/// A value that can be returned from functions
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub enum Value {
18    Null,
19    Boolean(bool),
20    Integer(i64),
21    Float(f64),
22    String(String),
23    Date(chrono::NaiveDate),
24    DateTime(chrono::NaiveDateTime),
25    Time(chrono::NaiveTime),
26}
27
28impl Value {
29    /// Convert to string representation
30    pub fn to_string(&self) -> String {
31        match self {
32            Value::Null => "NULL".to_string(),
33            Value::Boolean(b) => b.to_string(),
34            Value::Integer(i) => i.to_string(),
35            Value::Float(f) => f.to_string(),
36            Value::String(s) => s.clone(),
37            Value::Date(d) => d.format("%Y-%m-%d").to_string(),
38            Value::DateTime(dt) => dt.format("%Y-%m-%d %H:%M:%S").to_string(),
39            Value::Time(t) => t.format("%H:%M:%S").to_string(),
40        }
41    }
42
43    /// Try to convert to integer
44    pub fn as_i64(&self) -> Result<i64> {
45        match self {
46            Value::Integer(i) => Ok(*i),
47            Value::Float(f) => Ok(*f as i64),
48            Value::String(s) => s.parse().map_err(|_| {
49                DdbError::TypeError(format!("Cannot convert '{}' to integer", s))
50            }),
51            Value::Boolean(b) => Ok(if *b { 1 } else { 0 }),
52            _ => Err(DdbError::TypeError(format!(
53                "Cannot convert {:?} to integer",
54                self
55            ))),
56        }
57    }
58
59    /// Try to convert to float
60    pub fn as_f64(&self) -> Result<f64> {
61        match self {
62            Value::Float(f) => Ok(*f),
63            Value::Integer(i) => Ok(*i as f64),
64            Value::String(s) => s.parse().map_err(|_| {
65                DdbError::TypeError(format!("Cannot convert '{}' to float", s))
66            }),
67            _ => Err(DdbError::TypeError(format!(
68                "Cannot convert {:?} to float",
69                self
70            ))),
71        }
72    }
73
74    /// Check if value is null
75    pub fn is_null(&self) -> bool {
76        matches!(self, Value::Null)
77    }
78
79    /// Convert from string, attempting type inference
80    pub fn from_str(s: &str) -> Self {
81        // Try to parse as various types
82        if s.eq_ignore_ascii_case("null") || s.is_empty() {
83            return Value::Null;
84        }
85
86        if s.eq_ignore_ascii_case("true") {
87            return Value::Boolean(true);
88        }
89
90        if s.eq_ignore_ascii_case("false") {
91            return Value::Boolean(false);
92        }
93
94        // Try integer
95        if let Ok(i) = s.parse::<i64>() {
96            return Value::Integer(i);
97        }
98
99        // Try float
100        if let Ok(f) = s.parse::<f64>() {
101            return Value::Float(f);
102        }
103
104        // Default to string
105        Value::String(s.to_string())
106    }
107}
108
109impl std::fmt::Display for Value {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "{}", self.to_string())
112    }
113}
114
115impl From<&str> for Value {
116    fn from(s: &str) -> Self {
117        Value::String(s.to_string())
118    }
119}
120
121impl From<String> for Value {
122    fn from(s: String) -> Self {
123        Value::String(s)
124    }
125}
126
127impl From<i64> for Value {
128    fn from(i: i64) -> Self {
129        Value::Integer(i)
130    }
131}
132
133impl From<f64> for Value {
134    fn from(f: f64) -> Self {
135        Value::Float(f)
136    }
137}
138
139impl From<bool> for Value {
140    fn from(b: bool) -> Self {
141        Value::Boolean(b)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_value_conversions() {
151        assert_eq!(Value::from_str("123"), Value::Integer(123));
152        assert_eq!(Value::from_str("12.5"), Value::Float(12.5));
153        assert_eq!(Value::from_str("true"), Value::Boolean(true));
154        assert_eq!(Value::from_str("null"), Value::Null);
155        assert_eq!(Value::from_str("hello"), Value::String("hello".to_string()));
156    }
157
158    #[test]
159    fn test_value_to_string() {
160        assert_eq!(Value::Integer(42).to_string(), "42");
161        assert_eq!(Value::Float(3.14).to_string(), "3.14");
162        assert_eq!(Value::Boolean(true).to_string(), "true");
163        assert_eq!(Value::Null.to_string(), "NULL");
164    }
165}