wasmcloud_actor_graphdb/
results.rs

1use crate::client_type_error;
2use crate::errors::GraphResult;
3use crate::generated::*;
4
5pub const CAPID_GRAPHDB: &str = "wasmcloud:graphdb";
6
7/// Returns the number of columns in the result set.
8pub fn num_columns(rs: &ResultSet) -> usize {
9    rs.columns.len()
10}
11
12/// Returns the number of rows in the result set.
13pub fn num_rows(rs: &ResultSet) -> usize {
14    if let Some(col) = rs.columns.get(0) {
15        col.scalars.as_ref().unwrap_or(&vec![]).len()
16            + col.relations.as_ref().unwrap_or(&vec![]).len()
17            + col.nodes.as_ref().unwrap_or(&vec![]).len()
18    } else {
19        0
20    }
21}
22
23/// Returns the scalar at the given position.
24///
25/// Returns an error if the value at the given position is not a scalar
26/// or if the position is out of bounds.
27pub fn get_scalar(rs: &ResultSet, row_idx: usize, column_idx: usize) -> GraphResult<Scalar> {
28    match rs.columns.get(column_idx) {
29            Some(column) => match column.scalars.as_ref().unwrap_or(&vec![]).get(row_idx) {
30                Some(scalar) => Ok(scalar.clone()),
31                None => client_type_error!(
32                    "failed to get scalar: row index out of bounds: the len is {:?} but the index is {:?}", column.scalars.as_ref().unwrap_or(&vec![]).len(), row_idx,
33                ),
34            },
35            None => client_type_error!(
36                "failed to get scalar: column index out of bounds: the len is {:?} but the index is {:?}", rs.columns.len(), column_idx,
37            ),
38        }
39}
40
41/// Returns the node at the given position.
42///
43/// Returns an error if the value at the given position is not a node
44/// or if the position is out of bounds.
45pub fn get_node(rs: &ResultSet, row_idx: usize, column_idx: usize) -> GraphResult<Node> {
46    match rs.columns.get(column_idx) {
47            Some(column) => match column.nodes.as_ref().unwrap_or(&vec![]).get(row_idx) {
48                Some(node) => Ok(node.clone()),
49                None => client_type_error!(
50                    "failed to get node: row index out of bounds: the len is {:?} but the index is {:?}", column.nodes.as_ref().unwrap_or(&vec![]).len(), row_idx,
51                ),
52            },
53            None => client_type_error!(
54                "failed to get node: column index out of bounds: the len is {:?} but the index is {:?}", rs.columns.len(), column_idx,
55            ),
56        }
57}
58
59/// Returns the relation at the given position.
60///
61/// Returns an error if the value at the given position is not a relation
62/// or if the position is out of bounds.
63pub fn get_relation(rs: &ResultSet, row_idx: usize, column_idx: usize) -> GraphResult<Relation> {
64    match rs.columns.get(column_idx) {
65        Some(column) => match column.relations.as_ref().unwrap_or(&vec![]).get(row_idx) {
66            Some(relation) => Ok(relation.clone()),
67            None => client_type_error!(
68                "failed to get relation: row index out of bounds: the len is {:?} but the index is {:?}", column.relations.as_ref().unwrap_or(&vec![]).len(), row_idx,
69            ),
70        },
71        None => client_type_error!(
72            "failed to get relation: column index out of bounds: the len is {:?} but the index is {:?}", rs.columns.len(), column_idx,
73        ),
74    }
75}
76
77impl FromTable for ResultSet {
78    fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
79        Ok(result_set.clone())
80    }
81}
82
83impl<T: FromRow> FromTable for Vec<T> {
84    fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
85        let num_rows = num_rows(result_set);
86        let mut ret = Self::with_capacity(num_rows);
87
88        for i in 0..num_rows {
89            ret.push(T::from_row(result_set, i)?);
90        }
91
92        Ok(ret)
93    }
94}
95
96impl FromTable for () {
97    fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
98        Ok(())
99    }
100}
101
102pub trait FromTable: Sized {
103    fn from_table(result_set: &ResultSet) -> GraphResult<Self>;
104}
105
106/// Implemented by types that can be constructed from a row in a [`ResultSet`](../result_set/struct.ResultSet.html).
107pub trait FromRow: Sized {
108    fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<Self>;
109}
110
111/// Implemented by types that can be constructed from a cell in a [`ResultSet`](../result_set/struct.ResultSet.html).
112pub trait FromCell: Sized {
113    fn from_cell(result_set: &ResultSet, row_idx: usize, column_idx: usize) -> GraphResult<Self>;
114}
115
116// Macro generates generic "From" implementations to allow
117// tuples/vecs-of-tuples to be extracted from various types
118//
119// Altered version of https://github.com/mitsuhiko/redis-rs/blob/master/src/types.rs#L1080
120macro_rules! impl_row_for_tuple {
121    () => ();
122    ($($name:ident,)+) => (
123        #[doc(hidden)]
124        impl<$($name: FromCell),*> FromRow for ($($name,)*) {
125            // we have local variables named T1 as dummies and those
126            // variables are unused.
127            #[allow(non_snake_case, unused_variables, clippy::eval_order_dependence)]
128            fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<($($name,)*)> {
129                // hacky way to count the tuple size
130                let mut n = 0;
131                $(let $name = (); n += 1;)*
132                if num_columns(result_set) != n {
133                    return client_type_error!(
134                        "failed to construct tuple: tuple has {:?} entries but result table has {:?} columns",
135                        n,
136                        num_columns(result_set)
137                    );
138                }
139
140                // this is pretty ugly too. The { i += 1; i - 1 } is rust's
141                // postfix increment :)
142                let mut i = 0;
143                Ok(($({let $name = (); $name::from_cell(result_set, row_idx, { i += 1; i - 1 })?},)*))
144            }
145        }
146        impl_row_for_tuple_peel!($($name,)*);
147    )
148}
149
150// Support for the recursive macro calls
151macro_rules! impl_row_for_tuple_peel {
152    ($name:ident, $($other:ident,)*) => (impl_row_for_tuple!($($other,)*);)
153}
154
155// The library supports tuples of up to 12 items
156impl_row_for_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
157
158// Row and column indices default to zero for lower-level values
159impl<T: FromCell> FromRow for T {
160    fn from_row(result_set: &ResultSet, row_idx: usize) -> GraphResult<Self> {
161        T::from_cell(result_set, row_idx, 0)
162    }
163}
164
165impl<T: FromRow> FromTable for T {
166    fn from_table(result_set: &ResultSet) -> GraphResult<Self> {
167        T::from_row(result_set, 0)
168    }
169}
170
171#[cfg(test)]
172mod test {
173    use super::*;
174    use crate::generated::*;
175
176    // Verifies that we can extract the tuples we expect from the raw ResultSet
177    // structure and that the various return types are automatically converted
178    #[test]
179    fn tuple_extraction_test() {
180        let (name, birth_year): (String, u32) = fake_query("fake query").unwrap();
181        assert_eq!("tester", name);
182        assert_eq!(1985, birth_year);
183    }
184
185    #[test]
186    fn vec_tuple_extraction_test() {
187        let res: Vec<(String, u32)> = fake_vec_query("foo").unwrap();
188        assert_eq!(("tester".to_string(), 1985), res[0]);
189        assert_eq!(("test2".to_string(), 1986), res[1]);
190    }
191
192    fn fake_vec_query<T: FromTable>(_query: &str) -> GraphResult<T> {
193        query_with_statistics2().map(|(value, _)| value)
194    }
195
196    fn fake_query<T: FromTable>(_query: &str) -> GraphResult<T> {
197        query_with_statistics().map(|(value, _)| value)
198    }
199
200    fn query_with_statistics<T: FromTable>() -> GraphResult<(T, Vec<String>)> {
201        let result_set = get_result_set()?;
202        let value = T::from_table(&result_set)?;
203        Ok((value, result_set.statistics))
204    }
205
206    fn query_with_statistics2<T: FromTable>() -> GraphResult<(T, Vec<String>)> {
207        let result_set = get_result_set2()?;
208        let value = T::from_table(&result_set)?;
209        Ok((value, result_set.statistics))
210    }
211
212    fn get_result_set() -> GraphResult<ResultSet> {
213        Ok(ResultSet {
214            statistics: vec![],
215            columns: vec![
216                Column {
217                    scalars: Some(vec![Scalar {
218                        bool_value: None,
219                        double_value: None,
220                        int_value: None,
221                        string_value: Some("tester".to_string()),
222                    }]),
223                    nodes: Some(vec![]),
224                    relations: Some(vec![]),
225                },
226                Column {
227                    scalars: Some(vec![Scalar {
228                        bool_value: None,
229                        double_value: None,
230                        int_value: Some(1985),
231                        string_value: None,
232                    }]),
233                    nodes: Some(vec![]),
234                    relations: Some(vec![]),
235                },
236            ],
237        })
238    }
239
240    fn get_result_set2() -> GraphResult<ResultSet> {
241        Ok(ResultSet {
242            statistics: vec![],
243            columns: vec![
244                Column {
245                    scalars: Some(vec![
246                        Scalar {
247                            bool_value: None,
248                            double_value: None,
249                            int_value: None,
250                            string_value: Some("tester".to_string()),
251                        },
252                        Scalar {
253                            bool_value: None,
254                            double_value: None,
255                            int_value: None,
256                            string_value: Some("test2".to_string()),
257                        },
258                    ]),
259                    nodes: Some(vec![]),
260                    relations: Some(vec![]),
261                },
262                Column {
263                    scalars: Some(vec![
264                        Scalar {
265                            bool_value: None,
266                            double_value: None,
267                            int_value: Some(1985),
268                            string_value: None,
269                        },
270                        Scalar {
271                            bool_value: None,
272                            double_value: None,
273                            int_value: Some(1986),
274                            string_value: None,
275                        },
276                    ]),
277                    nodes: Some(vec![]),
278                    relations: Some(vec![]),
279                },
280            ],
281        })
282    }
283}