Skip to main content

nativeclick/convert/
raw_row.rs

1use std::borrow::Cow;
2
3use crate::{FromSql, NativeclickError, Result, Row, ToSql, Type, Value};
4
5/// A row of raw data returned from the database by a query.
6/// Or an unstructured runtime-defined row to upload to the server.
7#[derive(Debug, Default, Clone)]
8pub struct RawRow(Vec<Option<(String, Type, Value)>>);
9
10impl Row for RawRow {
11    const COLUMN_COUNT: Option<usize> = None;
12
13    fn column_names() -> Option<Vec<Cow<'static, str>>> {
14        None
15    }
16
17    fn deserialize_row(map: Vec<(&str, &Type, Value)>) -> Result<Self> {
18        Ok(Self(
19            map.into_iter()
20                .map(|(name, type_, value)| Some((name.to_string(), type_.clone(), value)))
21                .collect(),
22        ))
23    }
24
25    fn serialize_row(
26        self,
27        _type_hints: &indexmap::IndexMap<String, Type>,
28    ) -> Result<Vec<(Cow<'static, str>, Value)>> {
29        Ok(self
30            .0
31            .into_iter()
32            .map(|x| x.expect("cannot serialize a Row which has been retrieved from"))
33            .map(|(name, _, value)| (Cow::Owned(name), value))
34            .collect())
35    }
36}
37
38pub trait RowIndex {
39    fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize>;
40}
41
42impl RowIndex for usize {
43    fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
44        let count = columns.into_iter().count();
45        if count >= *self { Some(*self) } else { None }
46    }
47}
48
49impl RowIndex for str {
50    fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
51        columns.into_iter().position(|x| x == self)
52    }
53}
54
55impl<T: RowIndex + ?Sized> RowIndex for &T {
56    fn get<'a, I: IntoIterator<Item = &'a str>>(&self, columns: I) -> Option<usize> {
57        (*self).get(columns)
58    }
59}
60
61impl RawRow {
62    /// Determines if the row contains no values.
63    pub fn is_empty(&self) -> bool {
64        self.0.is_empty()
65    }
66
67    /// Returns the number of values in the row.
68    pub fn len(&self) -> usize {
69        self.0.len()
70    }
71
72    /// Like RawRow::get, but returns a Result rather than panicking.
73    pub fn try_get<I: RowIndex, T: FromSql>(&mut self, index: I) -> Result<T> {
74        let index = index
75            .get(
76                self.0
77                    .iter()
78                    .map(|x| x.as_ref().map(|x| &*x.0).unwrap_or("")),
79            )
80            .ok_or(NativeclickError::OutOfBounds)?;
81        let (_, type_, value) = self
82            .0
83            .get_mut(index)
84            .unwrap()
85            .take()
86            .ok_or(NativeclickError::DoubleFetch)?;
87        crate::from_sql_resolved(&type_, value)
88    }
89
90    /// Deserializes a value from the row.
91    /// The value can be specified either by its numeric index in the row, or by its column name.
92    /// # Panics
93    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
94    pub fn get<I: RowIndex, T: FromSql>(&mut self, index: I) -> T {
95        self.try_get(index).expect("failed to convert column")
96    }
97
98    /// Sets or inserts a column value with a given name. `type_` is inferred if `None`. Index is defined on insertion order.
99    pub fn try_set_typed(
100        &mut self,
101        name: impl ToString,
102        type_: Option<Type>,
103        value: impl ToSql,
104    ) -> Result<()> {
105        let name = name.to_string();
106        let value = value.to_sql(type_.as_ref())?;
107        let type_ = type_.unwrap_or_else(|| value.guess_type());
108
109        let current_position = self
110            .0
111            .iter()
112            .map(|x| x.as_ref().map(|x| &*x.0).unwrap_or(""))
113            .position(|x| x == &*name);
114
115        if let Some(current_position) = current_position {
116            self.0[current_position].as_mut().unwrap().1 = type_;
117            self.0[current_position].as_mut().unwrap().2 = value;
118        } else {
119            self.0.push(Some((name, type_, value)));
120        }
121        Ok(())
122    }
123
124    /// Same as `try_set_typed`, but always infers the type
125    pub fn try_set(&mut self, name: impl ToString, value: impl ToSql) -> Result<()> {
126        self.try_set_typed(name, None, value)
127    }
128
129    /// Same as `try_set`, but panics on type conversion failure.
130    pub fn set(&mut self, name: impl ToString, value: impl ToSql) {
131        self.try_set(name, value).expect("failed to convert column");
132    }
133
134    /// Same as `try_set_typed`, but panics on type conversion failure.
135    pub fn set_typed(&mut self, name: impl ToString, type_: Option<Type>, value: impl ToSql) {
136        self.try_set_typed(name, type_, value)
137            .expect("failed to convert column");
138    }
139}