rqlite_rs_core/
row.rs

1use std::{collections::HashMap, sync::Arc};
2
3use serde_json::Value;
4
5use crate::{column::Column, from_row::FromRow, IntoTypedError};
6
7#[derive(Debug)]
8pub struct Row {
9    values: Box<[Value]>,
10    columns: Arc<Vec<Column>>,
11    column_names: Arc<HashMap<String, usize>>,
12}
13
14impl Row {
15    #[must_use]
16    pub fn new(
17        columns: &Arc<Vec<Column>>,
18        column_names: &Arc<HashMap<String, usize>>,
19        values: Box<[Value]>,
20    ) -> Row {
21        Row {
22            values,
23            columns: Arc::clone(columns),
24            column_names: Arc::clone(column_names),
25        }
26    }
27
28    #[must_use]
29    pub fn columns(&self) -> &Arc<Vec<Column>> {
30        &self.columns
31    }
32
33    #[must_use]
34    pub fn column_names(&self) -> &Arc<HashMap<String, usize>> {
35        &self.column_names
36    }
37
38    /// Get a value by column name
39    ///
40    /// # Errors
41    /// If the column name is not found, returns `IntoTypedError::ColumnNotFound`
42    pub fn get<T: serde::de::DeserializeOwned>(&self, name: &str) -> Result<T, IntoTypedError> {
43        let index = self
44            .column_names
45            .get(name)
46            .ok_or(IntoTypedError::ColumnNotFound)?;
47
48        let value = self
49            .values
50            .get(*index)
51            .ok_or(IntoTypedError::ValueNotFound)?;
52
53        let value =
54            serde_json::from_value(value.clone()).map_err(IntoTypedError::ConversionError)?;
55
56        Ok(value)
57    }
58
59    /// Get a value by column name, returning `None` if the value is `null`
60    ///
61    /// # Errors
62    /// If the column name is not found, returns `IntoTypedError::ColumnNotFound`
63    pub fn get_opt<T: serde::de::DeserializeOwned>(
64        &self,
65        name: &str,
66    ) -> Result<Option<T>, IntoTypedError> {
67        let Some(index) = self.column_names.get(name) else {
68            return Ok(None);
69        };
70
71        // Throw error here, because if the column exists, the value should exist
72        let value = self
73            .values
74            .get(*index)
75            .ok_or(IntoTypedError::ValueNotFound)?;
76
77        if value == &Value::Null {
78            Ok(None)
79        } else {
80            let value =
81                serde_json::from_value(value.clone()).map_err(IntoTypedError::ConversionError)?;
82            Ok(Some(value))
83        }
84    }
85
86    /// Get a value by index
87    ///
88    /// # Errors
89    /// If the index is out of bounds, returns `IntoTypedError::ValueNotFound`
90    pub fn get_by_index<T: serde::de::DeserializeOwned>(
91        &self,
92        index: usize,
93    ) -> Result<T, IntoTypedError> {
94        let value = self
95            .values
96            .get(index)
97            .ok_or(IntoTypedError::ValueNotFound)?;
98        let value =
99            serde_json::from_value(value.clone()).map_err(IntoTypedError::ConversionError)?;
100        Ok(value)
101    }
102
103    /// Get a value by index, returning `None` if the value is `null`
104    ///
105    /// # Errors
106    /// If the index is out of bounds, returns `IntoTypedError::ValueNotFound`
107    pub fn get_by_index_opt<T: serde::de::DeserializeOwned>(
108        &self,
109        index: usize,
110    ) -> Result<Option<T>, IntoTypedError> {
111        let Some(value) = self.values.get(index) else {
112            return Ok(None);
113        };
114
115        if value == &Value::Null {
116            Ok(None)
117        } else {
118            let value =
119                serde_json::from_value(value.clone()).map_err(IntoTypedError::ConversionError)?;
120            Ok(Some(value))
121        }
122    }
123
124    #[must_use]
125    pub fn values(&self) -> &[Value] {
126        &self.values
127    }
128
129    #[must_use]
130    pub fn len(&self) -> usize {
131        self.values.len()
132    }
133
134    #[must_use]
135    pub fn is_empty(&self) -> bool {
136        self.values.is_empty()
137    }
138
139    /// Convert the row into a typed struct
140    ///
141    /// # Errors
142    /// If the conversion fails, returns `IntoTypedError`
143    pub fn into_typed<T>(self) -> Result<T, IntoTypedError>
144    where
145        T: FromRow,
146    {
147        T::from_row(self)
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    fn create_row() -> Row {
156        use serde_json::json;
157
158        let columns = Arc::new(vec![
159            Column::new("id".to_string(), 0usize, "integer".to_string()),
160            Column::new("name".to_string(), 1usize, "text".to_string()),
161        ]);
162
163        let column_names = Arc::new(
164            columns
165                .iter()
166                .enumerate()
167                .map(|(i, c)| (c.name().to_string(), i))
168                .collect::<HashMap<String, usize>>(),
169        );
170
171        let values = vec![json!(1), json!("test")].into_boxed_slice();
172
173        Row::new(&columns, &column_names, values)
174    }
175
176    #[test]
177    fn unit_row_get() {
178        let row = create_row();
179
180        assert_eq!(row.get::<i32>("id").unwrap(), 1);
181        assert_eq!(row.get::<String>("name").unwrap(), "test");
182        assert!(matches!(
183            row.get::<i32>("not_found").unwrap_err(),
184            IntoTypedError::ColumnNotFound
185        ));
186    }
187
188    #[test]
189    fn unit_row_get_opt() {
190        let row = create_row();
191
192        assert_eq!(row.get_opt::<i32>("id").unwrap(), Some(1));
193        assert_eq!(
194            row.get_opt::<String>("name").unwrap(),
195            Some("test".to_string())
196        );
197        assert_eq!(row.get_opt::<i32>("not_found").unwrap(), None);
198    }
199
200    #[test]
201    fn unit_row_get_by_index() {
202        let row = create_row();
203
204        assert_eq!(row.get_by_index::<i32>(0).unwrap(), 1);
205        assert_eq!(row.get_by_index::<String>(1).unwrap(), "test");
206        assert!(matches!(
207            row.get_by_index::<i32>(2).unwrap_err(),
208            IntoTypedError::ValueNotFound
209        ));
210    }
211
212    #[test]
213    fn unit_row_get_by_index_opt() {
214        let row = create_row();
215
216        assert_eq!(row.get_by_index_opt::<i32>(0).unwrap(), Some(1));
217        assert_eq!(
218            row.get_by_index_opt::<String>(1).unwrap(),
219            Some("test".to_string())
220        );
221        assert_eq!(row.get_by_index_opt::<i32>(2).unwrap(), None);
222    }
223
224    #[test]
225    fn unit_row_columns() {
226        let row = create_row();
227
228        assert_eq!(row.columns().len(), 2);
229        assert_eq!(row.columns()[0].name(), "id");
230        assert_eq!(row.columns()[1].name(), "name");
231    }
232
233    #[test]
234    fn unit_row_column_names() {
235        let row = create_row();
236
237        assert_eq!(row.column_names().len(), 2);
238        assert_eq!(row.column_names()["id"], 0);
239        assert_eq!(row.column_names()["name"], 1);
240    }
241
242    #[test]
243    fn unit_row_values() {
244        let row = create_row();
245
246        assert_eq!(row.values().len(), 2);
247        assert_eq!(row.values()[0], serde_json::json!(1));
248        assert_eq!(row.values()[1], serde_json::json!("test"));
249    }
250
251    #[test]
252    fn unit_row_len() {
253        let row = create_row();
254
255        assert_eq!(row.len(), 2);
256    }
257
258    #[test]
259    fn unit_row_is_empty() {
260        let row = create_row();
261
262        assert!(!row.is_empty());
263    }
264
265    #[test]
266    fn unit_row_into_typed_tuple() {
267        let row = create_row();
268
269        let (id, name) = row.into_typed::<(i32, String)>().unwrap();
270
271        assert_eq!(id, 1);
272        assert_eq!(name, "test");
273    }
274
275    #[test]
276    fn unit_row_into_typed_struct() {
277        struct Test {
278            id: i32,
279            name: String,
280        }
281
282        impl FromRow for Test {
283            fn from_row(row: Row) -> Result<Self, IntoTypedError> {
284                Ok(Test {
285                    id: row.get("id")?,
286                    name: row.get("name")?,
287                })
288            }
289        }
290
291        let row = create_row();
292
293        let test = row.into_typed::<Test>().unwrap();
294
295        assert_eq!(test.id, 1);
296        assert_eq!(test.name, "test");
297    }
298}