1use std::fmt;
4
5use crate::{OxiSqlError, Value};
6
7pub trait FromValue: Sized {
20 fn from_value(v: &Value) -> Result<Self, OxiSqlError>;
22}
23
24impl FromValue for bool {
25 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
26 match v {
27 Value::Bool(b) => Ok(*b),
28 other => Err(OxiSqlError::TypeMismatch {
29 expected: "Bool",
30 got: other.type_name(),
31 }),
32 }
33 }
34}
35
36impl FromValue for i32 {
37 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
38 match v {
39 Value::I64(n) => i32::try_from(*n).map_err(|_| OxiSqlError::TypeMismatch {
40 expected: "I64 (within i32 range)",
41 got: "I64 (out of i32 range)",
42 }),
43 other => Err(OxiSqlError::TypeMismatch {
44 expected: "I64",
45 got: other.type_name(),
46 }),
47 }
48 }
49}
50
51impl FromValue for i64 {
52 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
53 match v {
54 Value::I64(n) => Ok(*n),
55 other => Err(OxiSqlError::TypeMismatch {
56 expected: "I64",
57 got: other.type_name(),
58 }),
59 }
60 }
61}
62
63impl FromValue for f64 {
64 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
65 match v {
66 Value::F64(n) => Ok(*n),
67 Value::I64(n) => Ok(*n as f64),
69 other => Err(OxiSqlError::TypeMismatch {
70 expected: "F64",
71 got: other.type_name(),
72 }),
73 }
74 }
75}
76
77impl FromValue for String {
78 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
79 match v {
80 Value::Text(s) => Ok(s.clone()),
81 Value::Json(s) => Ok(s.clone()),
82 Value::Decimal(s) => Ok(s.clone()),
83 Value::Uuid(_) => Ok(format!("{v}")),
85 other => Err(OxiSqlError::TypeMismatch {
86 expected: "Text",
87 got: other.type_name(),
88 }),
89 }
90 }
91}
92
93impl FromValue for Vec<u8> {
94 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
95 match v {
96 Value::Blob(b) => Ok(b.clone()),
97 other => Err(OxiSqlError::TypeMismatch {
98 expected: "Blob",
99 got: other.type_name(),
100 }),
101 }
102 }
103}
104
105impl<T: FromValue> FromValue for Option<T> {
106 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
107 if v.is_null() {
108 Ok(None)
109 } else {
110 T::from_value(v).map(Some)
111 }
112 }
113}
114
115impl FromValue for u128 {
116 fn from_value(v: &Value) -> Result<Self, OxiSqlError> {
117 match v {
118 Value::Uuid(u) => Ok(*u),
119 other => Err(OxiSqlError::TypeMismatch {
120 expected: "Uuid",
121 got: other.type_name(),
122 }),
123 }
124 }
125}
126
127#[derive(Debug, Clone)]
131pub struct Row {
132 columns: Vec<String>,
133 values: Vec<Value>,
134 index: std::collections::HashMap<String, usize>,
135}
136
137impl Row {
138 pub fn new(columns: Vec<String>, values: Vec<Value>) -> Self {
145 let index = columns
146 .iter()
147 .enumerate()
148 .map(|(i, c)| (c.clone(), i))
149 .collect();
150 Self {
151 columns,
152 values,
153 index,
154 }
155 }
156
157 pub fn get(&self, col: &str) -> Option<&Value> {
160 self.index.get(col).and_then(|&i| self.values.get(i))
161 }
162
163 pub fn get_by_index(&self, i: usize) -> Option<&Value> {
165 self.values.get(i)
166 }
167
168 pub fn try_get<T: FromValue>(&self, col: &str) -> Result<T, OxiSqlError> {
183 let val = self
184 .get(col)
185 .ok_or_else(|| OxiSqlError::Other(format!("column '{col}' not found")))?;
186 T::from_value(val)
187 }
188
189 pub fn try_get_by_index<T: FromValue>(&self, i: usize) -> Result<T, OxiSqlError> {
193 let val = self
194 .get_by_index(i)
195 .ok_or_else(|| OxiSqlError::Other(format!("column index {i} out of range")))?;
196 T::from_value(val)
197 }
198
199 pub fn columns(&self) -> &[String] {
201 &self.columns
202 }
203
204 pub fn values(&self) -> &[Value] {
206 &self.values
207 }
208
209 pub fn column_count(&self) -> usize {
211 self.columns.len()
212 }
213
214 pub fn is_null(&self, col: &str) -> bool {
219 self.get(col).is_some_and(|v| v.is_null())
220 }
221
222 pub fn into_values(self) -> Vec<Value> {
224 self.values
225 }
226}
227
228impl fmt::Display for Row {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 write!(f, "{{")?;
231 for (i, (col, val)) in self.columns.iter().zip(self.values.iter()).enumerate() {
232 if i > 0 {
233 write!(f, ", ")?;
234 }
235 write!(f, "{col}: {val}")?;
236 }
237 write!(f, "}}")
238 }
239}
240
241#[derive(Debug, Clone)]
247pub struct RowSet {
248 columns: Vec<String>,
250 rows: Vec<Row>,
252}
253
254impl RowSet {
255 pub fn from_rows(rows: Vec<Row>) -> Self {
260 let columns = rows
261 .first()
262 .map(|r| r.columns().to_vec())
263 .unwrap_or_default();
264 Self { columns, rows }
265 }
266
267 pub fn new(columns: Vec<String>, rows: Vec<Row>) -> Self {
269 Self { columns, rows }
270 }
271
272 pub fn columns(&self) -> &[String] {
274 &self.columns
275 }
276
277 pub fn rows(&self) -> &[Row] {
279 &self.rows
280 }
281
282 pub fn len(&self) -> usize {
284 self.rows.len()
285 }
286
287 pub fn is_empty(&self) -> bool {
289 self.rows.is_empty()
290 }
291
292 pub fn column_count(&self) -> usize {
294 self.columns.len()
295 }
296
297 pub fn into_rows(self) -> Vec<Row> {
299 self.rows
300 }
301}