1use std::fmt;
2use std::cmp::Ordering;
3use std::hash::{Hash, Hasher};
4use serde::{Serialize, Deserialize};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub enum DbValue {
9 Null,
10 Integer(i64),
11 Real(f64),
12 Text(String),
13 Blob(Vec<u8>),
14 Boolean(bool),
15 Date(i64), Datetime(i64), }
18
19impl Hash for DbValue {
20 fn hash<H: Hasher>(&self, state: &mut H) {
21 match self {
23 DbValue::Null => 0u8.hash(state),
24 DbValue::Integer(_) => 1u8.hash(state),
25 DbValue::Real(_) => 2u8.hash(state),
26 DbValue::Text(_) => 3u8.hash(state),
27 DbValue::Blob(_) => 4u8.hash(state),
28 DbValue::Boolean(_) => 5u8.hash(state),
29 DbValue::Date(_) => 6u8.hash(state),
30 DbValue::Datetime(_) => 7u8.hash(state),
31 }
32 match self {
34 DbValue::Null => {}
35 DbValue::Integer(v) => v.hash(state),
36 DbValue::Real(v) => v.to_bits().hash(state),
37 DbValue::Text(v) => v.hash(state),
38 DbValue::Blob(v) => v.hash(state),
39 DbValue::Boolean(v) => v.hash(state),
40 DbValue::Date(v) => v.hash(state),
41 DbValue::Datetime(v) => v.hash(state),
42 }
43 }
44}
45
46impl PartialOrd for DbValue {
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 Some(self.cmp(other))
49 }
50}
51
52impl Ord for DbValue {
53 fn cmp(&self, other: &Self) -> Ordering {
54 let self_type_order = self.type_order();
56 let other_type_order = other.type_order();
57
58 match self_type_order.cmp(&other_type_order) {
59 Ordering::Equal => {
60 match (self, other) {
62 (DbValue::Null, DbValue::Null) => Ordering::Equal,
63 (DbValue::Integer(a), DbValue::Integer(b)) => a.cmp(b),
64 (DbValue::Real(a), DbValue::Real(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
65 (DbValue::Text(a), DbValue::Text(b)) => a.cmp(b),
66 (DbValue::Blob(a), DbValue::Blob(b)) => a.cmp(b),
67 (DbValue::Boolean(a), DbValue::Boolean(b)) => a.cmp(b),
68 (DbValue::Date(a), DbValue::Date(b)) => a.cmp(b),
69 (DbValue::Datetime(a), DbValue::Datetime(b)) => a.cmp(b),
70 _ => Ordering::Equal,
71 }
72 }
73 other_order => other_order,
74 }
75 }
76}
77
78impl Eq for DbValue {}
79
80impl DbValue {
81 fn type_order(&self) -> u8 {
83 match self {
84 DbValue::Null => 0,
85 DbValue::Integer(_) => 1,
86 DbValue::Real(_) => 2,
87 DbValue::Text(_) => 3,
88 DbValue::Blob(_) => 4,
89 DbValue::Boolean(_) => 5,
90 DbValue::Date(_) => 6,
91 DbValue::Datetime(_) => 7,
92 }
93 }
94 pub fn null() -> Self {
96 DbValue::Null
97 }
98
99 pub fn integer(v: i64) -> Self {
100 DbValue::Integer(v)
101 }
102
103 pub fn real(v: f64) -> Self {
104 DbValue::Real(v)
105 }
106
107 pub fn text(v: impl Into<String>) -> Self {
108 DbValue::Text(v.into())
109 }
110
111 pub fn blob(v: Vec<u8>) -> Self {
112 DbValue::Blob(v)
113 }
114
115 pub fn boolean(v: bool) -> Self {
116 DbValue::Boolean(v)
117 }
118
119 pub fn date(v: i64) -> Self {
120 DbValue::Date(v)
121 }
122
123 pub fn datetime(v: i64) -> Self {
124 DbValue::Datetime(v)
125 }
126
127 pub fn as_integer(&self) -> Option<i64> {
129 match self {
130 DbValue::Integer(v) => Some(*v),
131 _ => None,
132 }
133 }
134
135 pub fn as_real(&self) -> Option<f64> {
136 match self {
137 DbValue::Real(v) => Some(*v),
138 DbValue::Integer(v) => Some(*v as f64),
139 _ => None,
140 }
141 }
142
143 pub fn as_text(&self) -> Option<&str> {
144 match self {
145 DbValue::Text(s) => Some(s),
146 _ => None,
147 }
148 }
149
150 pub fn as_blob(&self) -> Option<&[u8]> {
151 match self {
152 DbValue::Blob(b) => Some(b),
153 _ => None,
154 }
155 }
156
157 pub fn as_boolean(&self) -> Option<bool> {
158 match self {
159 DbValue::Boolean(v) => Some(*v),
160 _ => None,
161 }
162 }
163
164 pub fn as_date(&self) -> Option<i64> {
165 match self {
166 DbValue::Date(v) => Some(*v),
167 _ => None,
168 }
169 }
170
171 pub fn as_datetime(&self) -> Option<i64> {
172 match self {
173 DbValue::Datetime(v) => Some(*v),
174 _ => None,
175 }
176 }
177
178 pub fn is_null(&self) -> bool {
180 matches!(self, DbValue::Null)
181 }
182
183 pub fn type_name(&self) -> &'static str {
185 match self {
186 DbValue::Null => "NULL",
187 DbValue::Integer(_) => "INTEGER",
188 DbValue::Real(_) => "REAL",
189 DbValue::Text(_) => "TEXT",
190 DbValue::Blob(_) => "BLOB",
191 DbValue::Boolean(_) => "BOOLEAN",
192 DbValue::Date(_) => "DATE",
193 DbValue::Datetime(_) => "DATETIME",
194 }
195 }
196}
197
198impl fmt::Display for DbValue {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 DbValue::Null => write!(f, "NULL"),
202 DbValue::Integer(v) => write!(f, "{}", v),
203 DbValue::Real(v) => write!(f, "{}", v),
204 DbValue::Text(s) => write!(f, "'{}'", s),
205 DbValue::Blob(b) => write!(f, "X'{}'", hex::encode(b)),
206 DbValue::Boolean(v) => write!(f, "{}", if *v { "TRUE" } else { "FALSE" }),
207 DbValue::Date(v) => write!(f, "DATE({})", v),
208 DbValue::Datetime(v) => write!(f, "DATETIME({})", v),
209 }
210 }
211}
212
213mod hex {
215 pub fn encode(data: &[u8]) -> String {
216 data.iter().map(|b| format!("{:02X}", b)).collect()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_integer_conversion() {
226 let v = DbValue::integer(42);
227 assert_eq!(v.as_integer(), Some(42));
228 assert_eq!(v.as_real(), Some(42.0));
229 assert_eq!(v.as_boolean(), None);
230 }
231
232 #[test]
233 fn test_text_conversion() {
234 let v = DbValue::text("hello");
235 assert_eq!(v.as_text(), Some("hello"));
236 assert_eq!(v.as_integer(), None);
237 }
238
239 #[test]
240 fn test_boolean_conversion() {
241 let v = DbValue::boolean(true);
242 assert_eq!(v.as_boolean(), Some(true));
243 }
244
245 #[test]
246 fn test_null() {
247 let v = DbValue::null();
248 assert!(v.is_null());
249 assert_eq!(v.as_integer(), None);
250 }
251}