Skip to main content

dactyl_db/
rows.rs

1//! Row projection returned by [`crate::read`] and [`crate::write`].
2
3use crate::error::DactylError;
4use serde::{Deserialize, Serialize};
5
6/// A collection of result rows.
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8pub struct Rows(pub Vec<Row>);
9
10impl Rows {
11    /// Borrow the rows as a slice.
12    pub fn as_slice(&self) -> &[Row] {
13        &self.0
14    }
15
16    /// Iterate over rows.
17    pub fn iter(&self) -> std::slice::Iter<'_, Row> {
18        self.0.iter()
19    }
20
21    /// Number of rows.
22    pub fn len(&self) -> usize {
23        self.0.len()
24    }
25
26    /// Whether the result is empty.
27    pub fn is_empty(&self) -> bool {
28        self.0.is_empty()
29    }
30}
31
32impl IntoIterator for Rows {
33    type Item = Row;
34    type IntoIter = std::vec::IntoIter<Row>;
35
36    fn into_iter(self) -> Self::IntoIter {
37        self.0.into_iter()
38    }
39}
40
41/// One result row. Carries the column names (shared across the result) plus
42/// the per-cell JSON values.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct Row {
45    /// Column names, in the order the adapter emitted them.
46    pub columns: Vec<String>,
47    /// Per-cell values, parallel to `columns`.
48    pub values: Vec<serde_json::Value>,
49}
50
51/// A unified database parameter value.
52#[derive(Debug, Clone, PartialEq)]
53pub enum Parameter {
54    Null,
55    Bool(bool),
56    Integer(i64),
57    Real(f64),
58    Text(String),
59}
60
61impl serde::Serialize for Parameter {
62    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: serde::Serializer,
65    {
66        match self {
67            Parameter::Null => serializer.serialize_unit(),
68            Parameter::Bool(b) => serializer.serialize_bool(*b),
69            Parameter::Integer(i) => serializer.serialize_i64(*i),
70            Parameter::Real(f) => serializer.serialize_f64(*f),
71            Parameter::Text(s) => serializer.serialize_str(s),
72        }
73    }
74}
75
76impl<'de> serde::Deserialize<'de> for Parameter {
77    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78    where
79        D: serde::Deserializer<'de>,
80    {
81        struct ParameterVisitor;
82        impl<'de> serde::de::Visitor<'de> for ParameterVisitor {
83            type Value = Parameter;
84            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
85                formatter.write_str("a database parameter value")
86            }
87            fn visit_none<E>(self) -> Result<Self::Value, E>
88            where
89                E: serde::de::Error,
90            {
91                Ok(Parameter::Null)
92            }
93            fn visit_unit<E>(self) -> Result<Self::Value, E>
94            where
95                E: serde::de::Error,
96            {
97                Ok(Parameter::Null)
98            }
99            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
100            where
101                E: serde::de::Error,
102            {
103                Ok(Parameter::Bool(v))
104            }
105            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
106            where
107                E: serde::de::Error,
108            {
109                Ok(Parameter::Integer(v))
110            }
111            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
112            where
113                E: serde::de::Error,
114            {
115                Ok(Parameter::Integer(v as i64))
116            }
117            fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
118            where
119                E: serde::de::Error,
120            {
121                Ok(Parameter::Real(v))
122            }
123            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
124            where
125                E: serde::de::Error,
126            {
127                Ok(Parameter::Text(v.to_string()))
128            }
129            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
130            where
131                E: serde::de::Error,
132            {
133                Ok(Parameter::Text(v))
134            }
135        }
136        deserializer.deserialize_any(ParameterVisitor)
137    }
138}
139
140impl From<i64> for Parameter {
141    fn from(v: i64) -> Self {
142        Parameter::Integer(v)
143    }
144}
145impl From<i32> for Parameter {
146    fn from(v: i32) -> Self {
147        Parameter::Integer(v as i64)
148    }
149}
150impl From<u32> for Parameter {
151    fn from(v: u32) -> Self {
152        Parameter::Integer(v as i64)
153    }
154}
155impl From<usize> for Parameter {
156    fn from(v: usize) -> Self {
157        Parameter::Integer(v as i64)
158    }
159}
160impl From<bool> for Parameter {
161    fn from(v: bool) -> Self {
162        Parameter::Bool(v)
163    }
164}
165impl From<f64> for Parameter {
166    fn from(v: f64) -> Self {
167        Parameter::Real(v)
168    }
169}
170impl From<String> for Parameter {
171    fn from(v: String) -> Self {
172        Parameter::Text(v)
173    }
174}
175impl From<&str> for Parameter {
176    fn from(v: &str) -> Self {
177        Parameter::Text(v.to_string())
178    }
179}
180
181impl From<Option<String>> for Parameter {
182    fn from(v: Option<String>) -> Self {
183        match v {
184            Some(s) => Parameter::Text(s),
185            None => Parameter::Null,
186        }
187    }
188}
189
190impl From<Option<&str>> for Parameter {
191    fn from(v: Option<&str>) -> Self {
192        match v {
193            Some(s) => Parameter::Text(s.to_string()),
194            None => Parameter::Null,
195        }
196    }
197}
198
199impl From<Option<i64>> for Parameter {
200    fn from(v: Option<i64>) -> Self {
201        match v {
202            Some(i) => Parameter::Integer(i),
203            None => Parameter::Null,
204        }
205    }
206}
207
208impl From<Option<bool>> for Parameter {
209    fn from(v: Option<bool>) -> Self {
210        match v {
211            Some(b) => Parameter::Bool(b),
212            None => Parameter::Null,
213        }
214    }
215}
216
217/// Helper trait for row indexing by position or column name.
218pub trait RowIndex: std::fmt::Debug {
219    /// Return index in row.
220    fn idx(&self, row: &Row) -> Option<usize>;
221}
222
223impl RowIndex for usize {
224    fn idx(&self, _row: &Row) -> Option<usize> {
225        Some(*self)
226    }
227}
228
229impl RowIndex for &str {
230    fn idx(&self, row: &Row) -> Option<usize> {
231        row.columns.iter().position(|c| c == self)
232    }
233}
234
235impl RowIndex for String {
236    fn idx(&self, row: &Row) -> Option<usize> {
237        row.columns.iter().position(|c| c == self)
238    }
239}
240
241impl Row {
242    /// Strict typed extraction via `serde`. Returns a `Conversion` error on
243    /// any type mismatch (e.g. reading an integer column as `bool` when the
244    /// stored JSON is `1` rather than `true`). For lenient portable shapes
245    /// use [`Self::get_bool`] / [`Self::get_int`] / [`Self::get_real`] /
246    /// [`Self::get_str`] / [`Self::get_json`].
247    ///
248    /// Missing column → [`DactylError::ColumnNotFound`].
249    pub fn get<I: RowIndex, T: serde::de::DeserializeOwned>(
250        &self,
251        index: I,
252    ) -> Result<T, DactylError> {
253        let i = self.idx(&index)?;
254        let val = &self.values[i];
255        serde_json::from_value(val.clone()).map_err(|e| {
256            DactylError::Conversion(format!(
257                "failed to convert column {:?} to target type: {}",
258                index, e
259            ))
260        })
261    }
262
263    /// Lenient `bool` accessor: accepts `true`/`false` or `0`/`1` integer.
264    pub fn get_bool<I: RowIndex>(&self, index: I) -> Result<bool, DactylError> {
265        let i = self.idx(&index)?;
266        match &self.values[i] {
267            serde_json::Value::Bool(b) => Ok(*b),
268            serde_json::Value::Number(n) if n.as_i64() == Some(0) => Ok(false),
269            serde_json::Value::Number(n) if n.as_i64() == Some(1) => Ok(true),
270            other => Err(DactylError::Conversion(format!(
271                "cannot read {other:?} as bool at column {:?}",
272                index
273            ))),
274        }
275    }
276
277    /// Lenient `i64` accessor: accepts JSON integer.
278    pub fn get_int<I: RowIndex>(&self, index: I) -> Result<i64, DactylError> {
279        let i = self.idx(&index)?;
280        match &self.values[i] {
281            serde_json::Value::Number(n) => n.as_i64().ok_or_else(|| {
282                DactylError::Conversion(format!("value is not i64 at column {:?}", index))
283            }),
284            other => Err(DactylError::Conversion(format!(
285                "cannot read {other:?} as i64 at column {:?}",
286                index
287            ))),
288        }
289    }
290
291    /// Lenient `f64` accessor: accepts JSON number.
292    pub fn get_real<I: RowIndex>(&self, index: I) -> Result<f64, DactylError> {
293        let i = self.idx(&index)?;
294        match &self.values[i] {
295            serde_json::Value::Number(n) => n.as_f64().ok_or_else(|| {
296                DactylError::Conversion(format!("value is not f64 at column {:?}", index))
297            }),
298            other => Err(DactylError::Conversion(format!(
299                "cannot read {other:?} as f64 at column {:?}",
300                index
301            ))),
302        }
303    }
304
305    /// Lenient `String` accessor: accepts JSON string.
306    pub fn get_str<I: RowIndex>(&self, index: I) -> Result<String, DactylError> {
307        let i = self.idx(&index)?;
308        match &self.values[i] {
309            serde_json::Value::String(s) => Ok(s.clone()),
310            other => Err(DactylError::Conversion(format!(
311                "cannot read {other:?} as String at column {:?}",
312                index
313            ))),
314        }
315    }
316
317    /// Raw JSON value accessor at the given column.
318    pub fn get_json<I: RowIndex>(&self, index: I) -> Result<serde_json::Value, DactylError> {
319        let i = self.idx(&index)?;
320        Ok(self.values[i].clone())
321    }
322
323    fn idx<I: RowIndex>(&self, index: &I) -> Result<usize, DactylError> {
324        index
325            .idx(self)
326            .ok_or_else(|| DactylError::ColumnNotFound(format!("{:?}", index)))
327            .and_then(|i| {
328                if i < self.values.len() {
329                    Ok(i)
330                } else {
331                    Err(DactylError::ColumnNotFound(format!(
332                        "index {i} out of bounds"
333                    )))
334                }
335            })
336    }
337}