use crate::client::{Client, FromColumnIndexed, FromColumnNamed};
use crate::error::Error;
#[derive(Debug)]
pub struct ColumnsIndexed<'a, 'b, C: Client> {
row: &'a C::Row<'b>,
offset: usize,
}
impl<'a, 'b, C: Client> ColumnsIndexed<'a, 'b, C> {
pub fn new(row: &'a C::Row<'b>) -> Self {
ColumnsIndexed { row, offset: 0 }
}
pub fn get<T>(&self, index: usize) -> Result<T, Error<C::Error>>
where
T: FromColumnIndexed<C>,
{
FromColumnIndexed::from_column(self.row, self.offset + index)
}
pub fn get_nested<T>(&self, offset: usize) -> Result<T, Error<C::Error>>
where
T: FromColumnsIndexed<C>,
{
FromColumnsIndexed::from_columns(self.child(offset))
}
fn child(&self, offset: usize) -> Self {
let offset = self.offset + offset;
ColumnsIndexed {
row: self.row,
offset,
}
}
}
#[derive(Debug)]
pub struct ColumnsNamed<'a, 'b, C: Client> {
row: &'a C::Row<'b>,
prefix: String,
}
impl<'a, 'b, C: Client> ColumnsNamed<'a, 'b, C> {
pub fn new(row: &'a C::Row<'b>) -> Self {
ColumnsNamed {
row,
prefix: String::new(),
}
}
pub fn get<T>(&self, name: &str) -> Result<T, Error<C::Error>>
where
T: FromColumnNamed<C>,
{
let name = {
let mut s = self.prefix.clone();
s.push_str(name);
s
};
FromColumnNamed::from_column(self.row, name.as_ref())
}
pub fn get_nested<T>(&self, prefix: &str) -> Result<T, Error<C::Error>>
where
T: FromColumnsNamed<C>,
{
FromColumnsNamed::from_columns(self.child(prefix))
}
fn child(&self, prefix: &str) -> Self {
let prefix = {
let mut s = self.prefix.clone();
s.push_str(prefix);
s
};
ColumnsNamed {
row: self.row,
prefix,
}
}
}
#[cfg_attr(
feature = "derive",
doc = r##"
```
# use aykroyd::{FromRow, Query};
# use aykroyd::row::FromColumnsIndexed;
# struct Color;
#[derive(FromColumnsIndexed)]
struct Person {
name: String,
favorite_color: Color,
}
#[derive(FromRow)]
#[aykroyd(by_index)]
struct Pet {
name: String,
#[aykroyd(nested)]
owner: Person,
}
#[derive(Query)]
#[aykroyd(row(Pet), text = "
SELECT pet.name, owner.name, owner.fav_color FROM pets
")]
struct GetPets;
```
"##
)]
pub trait FromColumnsIndexed<C: Client>: Sized {
const NUM_COLUMNS: usize;
fn from_columns(columns: ColumnsIndexed<C>) -> Result<Self, Error<C::Error>>;
}
impl<C: Client, T: FromColumnsIndexed<C>> FromColumnsIndexed<C> for Option<T> {
const NUM_COLUMNS: usize = T::NUM_COLUMNS;
fn from_columns(columns: ColumnsIndexed<C>) -> Result<Self, Error<C::Error>> {
T::from_columns(columns).map(Some).or(Ok(None)) }
}
#[cfg_attr(
feature = "derive",
doc = r##"
```
# use aykroyd::{FromRow, Query};
# use aykroyd::row::FromColumnsNamed;
# struct Color;
#[derive(FromColumnsNamed)]
struct Person {
name: String,
favorite_color: Color,
}
#[derive(FromRow)]
struct Pet {
name: String,
#[aykroyd(nested)]
owner: Person,
}
#[derive(Query)]
#[aykroyd(row(Pet), text = "
SELECT pet.name, owner.name AS owner_name, owner.fav_color AS owner_favorite_color FROM pets
")]
struct GetPets;
```
"##
)]
pub trait FromColumnsNamed<C: Client>: Sized {
fn from_columns(columns: ColumnsNamed<C>) -> Result<Self, Error<C::Error>>;
}
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use aykroyd_derive::{FromColumnsIndexed, FromColumnsNamed};
macro_rules! impl_tuple_from_columns_indexed {
(
$num_columns:literal $(:)?
$(
$name:ident $index:literal
),*
$(,)?
) => {
impl<
C,
$(
$name,
)*
> FromColumnsIndexed<C> for ($($name,)*)
where
C: Client,
$(
$name: FromColumnIndexed<C>,
)*
{
const NUM_COLUMNS: usize = $num_columns;
#[allow(unused_variables)]
fn from_columns(
columns: ColumnsIndexed<C>,
) -> Result<Self, Error<C::Error>> {
Ok(($(
columns.get($index)?,
)*))
}
}
};
}
impl_tuple_from_columns_indexed!(0);
impl_tuple_from_columns_indexed!(1: T0 0);
impl_tuple_from_columns_indexed!(2: T0 0, T1 1);
impl_tuple_from_columns_indexed!(3: T0 0, T1 1, T2 2);
impl_tuple_from_columns_indexed!(4: T0 0, T1 1, T2 2, T3 3);
impl_tuple_from_columns_indexed!(5: T0 0, T1 1, T2 2, T3 3, T4 4);
impl_tuple_from_columns_indexed!(6: T0 0, T1 1, T2 2, T3 3, T4 4, T5 5);
impl_tuple_from_columns_indexed!(7: T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6);
impl_tuple_from_columns_indexed!(8: T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7);
#[cfg(test)]
mod test {
use super::*;
use crate::test::sync_client::{self, TestClient};
#[test]
fn columns_indexed_get() {
fn test(columns: &ColumnsIndexed<TestClient>, index: usize, expected: &str) {
let actual: String = columns.get(index).unwrap();
assert_eq!(expected, actual);
}
let mut client = TestClient::new();
let row = client.row(sync_client::RowInner {
names: vec!["name".into(), "age".into(), "superpower".into()],
values: vec!["Hermes".into(), "42".into(), "Filing".into()],
});
let columns = ColumnsIndexed::new(&row);
test(&columns, 0, "Hermes");
test(&columns, 1, "42");
test(&columns, 2, "Filing");
}
#[test]
fn columns_indexed_get_nested() {
#[derive(PartialEq, Eq, Debug)]
struct Nested(String, String, String);
impl FromColumnsIndexed<TestClient> for Nested {
const NUM_COLUMNS: usize = 3;
fn from_columns(columns: ColumnsIndexed<TestClient>) -> sync_client::Result<Self> {
Ok(Nested(columns.get(0)?, columns.get(1)?, columns.get(2)?))
}
}
fn test(columns: &ColumnsIndexed<TestClient>, expected: Nested) {
let actual: Nested = columns.get_nested(1).unwrap();
assert_eq!(expected, actual);
}
let mut client = TestClient::new();
let row = client.row(sync_client::RowInner {
names: vec![
"something_else".into(),
"character_name".into(),
"character_age".into(),
"character_superpower".into(),
],
values: vec![
"Hello".into(),
"Hermes".into(),
"42".into(),
"Filing".into(),
],
});
let columns = ColumnsIndexed::new(&row);
test(
&columns,
Nested("Hermes".into(), "42".into(), "Filing".into()),
);
}
#[test]
fn columns_named_get() {
fn test(columns: &ColumnsNamed<TestClient>, name: &str, expected: &str) {
let actual: String = columns.get(name).unwrap();
assert_eq!(expected, actual);
}
let mut client = TestClient::new();
let row = client.row(sync_client::RowInner {
names: vec!["name".into(), "age".into(), "superpower".into()],
values: vec!["Hermes".into(), "42".into(), "Filing".into()],
});
let columns = ColumnsNamed::new(&row);
test(&columns, "name", "Hermes");
test(&columns, "age", "42");
test(&columns, "superpower", "Filing");
}
#[test]
fn columns_named_get_nested() {
#[derive(PartialEq, Eq, Debug)]
struct Nested(String, String, String);
impl FromColumnsNamed<TestClient> for Nested {
fn from_columns(columns: ColumnsNamed<TestClient>) -> sync_client::Result<Self> {
Ok(Nested(
columns.get("name")?,
columns.get("age")?,
columns.get("superpower")?,
))
}
}
fn test(columns: &ColumnsNamed<TestClient>, expected: Nested) {
let actual: Nested = columns.get_nested("character_").unwrap();
assert_eq!(expected, actual);
}
let mut client = TestClient::new();
let row = client.row(sync_client::RowInner {
names: vec![
"something_else".into(),
"character_name".into(),
"character_age".into(),
"character_superpower".into(),
],
values: vec![
"Hello".into(),
"Hermes".into(),
"42".into(),
"Filing".into(),
],
});
let columns = ColumnsNamed::new(&row);
test(
&columns,
Nested("Hermes".into(), "42".into(), "Filing".into()),
);
}
}