1use azul_css::{AzString, StringVec, U8Vec};
15
16#[repr(C, u8)]
20#[derive(Debug, Clone, PartialEq)]
21pub enum DbValue {
22 Null,
24 Integer(i64),
26 Real(f64),
28 Text(AzString),
30 Blob(U8Vec),
32}
33
34impl DbValue {
35 pub fn is_null(&self) -> bool {
36 matches!(self, DbValue::Null)
37 }
38 pub fn as_integer(&self) -> Option<i64> {
39 if let DbValue::Integer(i) = self {
40 Some(*i)
41 } else {
42 None
43 }
44 }
45 pub fn as_real(&self) -> Option<f64> {
46 if let DbValue::Real(r) = self {
47 Some(*r)
48 } else {
49 None
50 }
51 }
52 pub fn as_text(&self) -> Option<&AzString> {
53 if let DbValue::Text(t) = self {
54 Some(t)
55 } else {
56 None
57 }
58 }
59}
60
61impl_vec!(
62 DbValue,
63 DbValueVec,
64 DbValueVecDestructor,
65 DbValueVecDestructorType,
66 DbValueVecSlice,
67 OptionDbValue
68);
69impl_vec_debug!(DbValue, DbValueVec);
70impl_vec_clone!(DbValue, DbValueVec, DbValueVecDestructor);
71impl_vec_partialeq!(DbValue, DbValueVec);
72impl_option!(DbValue, OptionDbValue, copy = false, [Debug, Clone, PartialEq]);
73
74#[repr(C)]
78#[derive(Debug, Clone, PartialEq)]
79pub struct DbRows {
80 pub columns: StringVec,
82 pub values: DbValueVec,
84}
85
86impl DbRows {
87 pub fn num_columns(&self) -> usize {
89 self.columns.as_ref().len()
90 }
91 pub fn num_rows(&self) -> usize {
93 let cols = self.num_columns();
94 if cols == 0 {
95 0
96 } else {
97 self.values.as_ref().len() / cols
98 }
99 }
100 pub fn get(&self, row: usize, col: usize) -> Option<&DbValue> {
102 let cols = self.num_columns();
103 if col >= cols {
104 return None;
105 }
106 self.values.as_ref().get(row * cols + col)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn dbvalue_accessors() {
116 assert!(DbValue::Null.is_null());
117 assert_eq!(DbValue::Integer(7).as_integer(), Some(7));
118 assert_eq!(DbValue::Real(1.5).as_real(), Some(1.5));
119 assert_eq!(
120 DbValue::Text(AzString::from_const_str("hi")).as_text().map(|s| s.as_str()),
121 Some("hi")
122 );
123 assert_eq!(DbValue::Null.as_integer(), None);
125 assert!(!DbValue::Integer(0).is_null());
126 }
127
128 #[test]
129 fn dbrows_indexing() {
130 let columns = StringVec::from_vec(vec![
132 AzString::from_const_str("id"),
133 AzString::from_const_str("name"),
134 ]);
135 let values = DbValueVec::from_vec(vec![
136 DbValue::Integer(1),
137 DbValue::Text(AzString::from_const_str("alice")),
138 DbValue::Integer(2),
139 DbValue::Text(AzString::from_const_str("bob")),
140 ]);
141 let rows = DbRows { columns, values };
142
143 assert_eq!(rows.num_columns(), 2);
144 assert_eq!(rows.num_rows(), 2);
145 assert_eq!(rows.get(0, 0).and_then(|v| v.as_integer()), Some(1));
146 assert_eq!(
147 rows.get(1, 1).and_then(|v| v.as_text()).map(|s| s.as_str()),
148 Some("bob")
149 );
150 assert!(rows.get(0, 2).is_none());
152 assert!(rows.get(2, 0).is_none());
153 }
154
155 #[test]
156 fn dbrows_empty() {
157 let rows = DbRows {
158 columns: StringVec::from_vec(vec![]),
159 values: DbValueVec::from_vec(vec![]),
160 };
161 assert_eq!(rows.num_columns(), 0);
162 assert_eq!(rows.num_rows(), 0);
163 assert!(rows.get(0, 0).is_none());
164 }
165}