Skip to main content

vespertide_core/schema/
str_or_bool.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
5#[serde(rename_all = "snake_case", untagged)]
6pub enum StrOrBoolOrArray {
7    Str(String),
8    Array(Vec<String>),
9    Bool(bool),
10}
11
12/// A value that can be a string, boolean, or number.
13/// This is used for default values where columns can use literal values directly.
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[serde(untagged)]
17pub enum DefaultValue {
18    Bool(bool),
19    Integer(i64),
20    Float(f64),
21    String(String),
22}
23
24impl Eq for DefaultValue {}
25
26impl DefaultValue {
27    /// Convert to SQL string representation
28    /// Empty strings are converted to '' (SQL empty string literal)
29    pub fn to_sql(&self) -> String {
30        match self {
31            DefaultValue::Bool(b) => b.to_string(),
32            DefaultValue::Integer(n) => n.to_string(),
33            DefaultValue::Float(f) => f.to_string(),
34            DefaultValue::String(s) => {
35                if s.is_empty() {
36                    "''".to_string()
37                } else {
38                    s.clone()
39                }
40            }
41        }
42    }
43
44    /// Check if this is a string type (needs quoting for certain column types)
45    pub fn is_string(&self) -> bool {
46        matches!(self, DefaultValue::String(_))
47    }
48
49    /// Check if this is an empty string
50    pub fn is_empty_string(&self) -> bool {
51        matches!(self, DefaultValue::String(s) if s.is_empty())
52    }
53}
54
55impl From<bool> for DefaultValue {
56    fn from(b: bool) -> Self {
57        DefaultValue::Bool(b)
58    }
59}
60
61impl From<i64> for DefaultValue {
62    fn from(n: i64) -> Self {
63        DefaultValue::Integer(n)
64    }
65}
66
67impl From<i32> for DefaultValue {
68    fn from(n: i32) -> Self {
69        DefaultValue::Integer(n as i64)
70    }
71}
72
73impl From<f64> for DefaultValue {
74    fn from(f: f64) -> Self {
75        DefaultValue::Float(f)
76    }
77}
78
79impl From<String> for DefaultValue {
80    fn from(s: String) -> Self {
81        DefaultValue::String(s)
82    }
83}
84
85impl From<&str> for DefaultValue {
86    fn from(s: &str) -> Self {
87        DefaultValue::String(s.to_string())
88    }
89}
90
91/// Backwards compatibility alias
92pub type StringOrBool = DefaultValue;
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_default_value_to_sql_bool() {
100        let val = DefaultValue::Bool(true);
101        assert_eq!(val.to_sql(), "true");
102
103        let val = DefaultValue::Bool(false);
104        assert_eq!(val.to_sql(), "false");
105    }
106
107    #[test]
108    fn test_default_value_to_sql_integer() {
109        let val = DefaultValue::Integer(42);
110        assert_eq!(val.to_sql(), "42");
111
112        let val = DefaultValue::Integer(-100);
113        assert_eq!(val.to_sql(), "-100");
114    }
115
116    #[test]
117    fn test_default_value_to_sql_float() {
118        let val = DefaultValue::Float(1.5);
119        assert_eq!(val.to_sql(), "1.5");
120    }
121
122    #[test]
123    fn test_default_value_to_sql_string() {
124        let val = DefaultValue::String("hello".into());
125        assert_eq!(val.to_sql(), "hello");
126    }
127
128    #[test]
129    fn test_default_value_to_sql_empty_string() {
130        let val = DefaultValue::String("".into());
131        assert_eq!(val.to_sql(), "''");
132    }
133
134    #[test]
135    fn test_default_value_is_empty_string() {
136        assert!(DefaultValue::String("".into()).is_empty_string());
137        assert!(!DefaultValue::String("hello".into()).is_empty_string());
138        assert!(!DefaultValue::Bool(true).is_empty_string());
139        assert!(!DefaultValue::Integer(0).is_empty_string());
140    }
141
142    #[test]
143    fn test_default_value_from_bool() {
144        let val: DefaultValue = true.into();
145        assert_eq!(val, DefaultValue::Bool(true));
146
147        let val: DefaultValue = false.into();
148        assert_eq!(val, DefaultValue::Bool(false));
149    }
150
151    #[test]
152    fn test_default_value_from_integer() {
153        let val: DefaultValue = 42i64.into();
154        assert_eq!(val, DefaultValue::Integer(42));
155
156        let val: DefaultValue = 100i32.into();
157        assert_eq!(val, DefaultValue::Integer(100));
158    }
159
160    #[test]
161    fn test_default_value_from_float() {
162        let val: DefaultValue = 1.5f64.into();
163        assert_eq!(val, DefaultValue::Float(1.5));
164    }
165
166    #[test]
167    fn test_default_value_from_string() {
168        let val: DefaultValue = String::from("test").into();
169        assert_eq!(val, DefaultValue::String("test".into()));
170    }
171
172    #[test]
173    fn test_default_value_from_str() {
174        let val: DefaultValue = "test".into();
175        assert_eq!(val, DefaultValue::String("test".into()));
176    }
177
178    #[test]
179    fn test_default_value_is_string() {
180        assert!(DefaultValue::String("test".into()).is_string());
181        assert!(!DefaultValue::Bool(true).is_string());
182        assert!(!DefaultValue::Integer(42).is_string());
183        assert!(!DefaultValue::Float(1.5).is_string());
184    }
185}