mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
impl TryFrom<&Row<'_>> for (...)
This change implements `TryFrom<&Row>` for tuples up to 16 fields. This is a convenience function that can be used to map rows more easily. The change includes tests for 1-tuple, 2-tuple and 16-tuple.
This commit is contained in:
parent
aabcce9aa6
commit
c5b2efc099
196
src/row.rs
196
src/row.rs
@ -334,3 +334,199 @@ impl RowIndex for &'_ str {
|
||||
stmt.column_index(*self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuple_try_from_row {
|
||||
($($field:ident),*) => {
|
||||
impl<'a, 'b, $($field,)*> convert::TryFrom<&'a Row<'b>> for ($($field,)*) where $($field: FromSql,)* {
|
||||
type Error = crate::Error;
|
||||
|
||||
// we end with index += 1, which rustc warns about
|
||||
// unused_variables and unused_mut are allowed for ()
|
||||
#[allow(unused_assignments, unused_variables, unused_mut)]
|
||||
fn try_from(row: &'a Row<'b>) -> Result<Self> {
|
||||
let mut index = 0;
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
let $field = row.get::<_, $field>(index)?;
|
||||
index += 1;
|
||||
)*
|
||||
Ok(($($field,)*))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuples_try_from_row {
|
||||
() => {
|
||||
// not very useful, but maybe some other macro users will find this helpful
|
||||
tuple_try_from_row!();
|
||||
};
|
||||
($first:ident $(, $remaining:ident)*) => {
|
||||
tuple_try_from_row!($first $(, $remaining)*);
|
||||
tuples_try_from_row!($($remaining),*);
|
||||
};
|
||||
}
|
||||
|
||||
tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_try_from_row_for_tuple_1() {
|
||||
use crate::{Connection, ToSql};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
|
||||
conn.execute(
|
||||
"CREATE TABLE test (a INTEGER)",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
)
|
||||
.expect("failed to create table");
|
||||
conn.execute(
|
||||
"INSERT INTO test VALUES (42)",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
)
|
||||
.expect("failed to insert value");
|
||||
let val = conn
|
||||
.query_row(
|
||||
"SELECT a FROM test",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
|row| <(u32,)>::try_from(row),
|
||||
)
|
||||
.expect("failed to query row");
|
||||
assert_eq!(val, (42,));
|
||||
let fail = conn.query_row(
|
||||
"SELECT a FROM test",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
|row| <(u32, u32)>::try_from(row),
|
||||
);
|
||||
assert!(fail.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_row_for_tuple_2() {
|
||||
use crate::{Connection, ToSql};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
|
||||
conn.execute(
|
||||
"CREATE TABLE test (a INTEGER, b INTEGER)",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
)
|
||||
.expect("failed to create table");
|
||||
conn.execute(
|
||||
"INSERT INTO test VALUES (42, 47)",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
)
|
||||
.expect("failed to insert value");
|
||||
let val = conn
|
||||
.query_row(
|
||||
"SELECT a, b FROM test",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
|row| <(u32, u32)>::try_from(row),
|
||||
)
|
||||
.expect("failed to query row");
|
||||
assert_eq!(val, (42, 47));
|
||||
let fail = conn.query_row(
|
||||
"SELECT a, b FROM test",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
|row| <(u32, u32, u32)>::try_from(row),
|
||||
);
|
||||
assert!(fail.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from_row_for_tuple_16() {
|
||||
use crate::{Connection, ToSql};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let create_table = "CREATE TABLE test (
|
||||
a INTEGER,
|
||||
b INTEGER,
|
||||
c INTEGER,
|
||||
d INTEGER,
|
||||
e INTEGER,
|
||||
f INTEGER,
|
||||
g INTEGER,
|
||||
h INTEGER,
|
||||
i INTEGER,
|
||||
j INTEGER,
|
||||
k INTEGER,
|
||||
l INTEGER,
|
||||
m INTEGER,
|
||||
n INTEGER,
|
||||
o INTEGER,
|
||||
p INTEGER
|
||||
)";
|
||||
|
||||
let insert_values = "INSERT INTO test VALUES (
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
)";
|
||||
|
||||
type BigTuple = (
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
);
|
||||
|
||||
let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
|
||||
conn.execute(create_table, std::iter::empty::<&dyn ToSql>())
|
||||
.expect("failed to create table");
|
||||
conn.execute(insert_values, std::iter::empty::<&dyn ToSql>())
|
||||
.expect("failed to insert value");
|
||||
let val = conn
|
||||
.query_row(
|
||||
"SELECT * FROM test",
|
||||
std::iter::empty::<&dyn ToSql>(),
|
||||
|row| BigTuple::try_from(row),
|
||||
)
|
||||
.expect("failed to query row");
|
||||
// Debug is not implemented for tuples of 16
|
||||
assert_eq!(val.0, 0);
|
||||
assert_eq!(val.1, 1);
|
||||
assert_eq!(val.2, 2);
|
||||
assert_eq!(val.3, 3);
|
||||
assert_eq!(val.4, 4);
|
||||
assert_eq!(val.5, 5);
|
||||
assert_eq!(val.6, 6);
|
||||
assert_eq!(val.7, 7);
|
||||
assert_eq!(val.8, 8);
|
||||
assert_eq!(val.9, 9);
|
||||
assert_eq!(val.10, 10);
|
||||
assert_eq!(val.11, 11);
|
||||
assert_eq!(val.12, 12);
|
||||
assert_eq!(val.13, 13);
|
||||
assert_eq!(val.14, 14);
|
||||
assert_eq!(val.15, 15);
|
||||
|
||||
// We don't test one bigger because it's unimplemented
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user