1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use super::{Result, Statement};

use libc::{c_double, c_int};
use sqlite3_sys as ffi;

/// Collectable types can be parsed from the columns of the sqlite result row
pub trait Collectable
where
    Self: Sized,
{
    /// collects itself and increments to next column
    fn collect(statement: &Statement, column: &mut c_int) -> Self;

    fn step_and_collect(statement: &mut Statement) -> Result<Self> {
        let retcode = unsafe { ffi::sqlite3_step(statement.stmt) };

        match retcode {
            ffi::SQLITE_ROW => Ok(Self::collect(statement, &mut 0)),
            other => Err(other.into()),
        }
    }

    /// number of columns needed by this type
    fn columns_needed() -> c_int;
}

impl Collectable for () {
    fn collect(_statement: &Statement, _column: &mut c_int) -> Self {}
    fn columns_needed() -> c_int {
        0
    }
}
impl<T> Collectable for Option<T>
where
    T: Collectable,
{
    fn collect(statement: &Statement, column: &mut c_int) -> Self {
        let sqlite_type = unsafe { ffi::sqlite3_column_type(statement.stmt, *column) };
        match sqlite_type {
            ffi::SQLITE_NULL => {
                *column += 1;
                None
            }
            _ => Some(T::collect(statement, column)),
        }
    }
    fn step_and_collect(statement: &mut Statement) -> Result<Self> {
        let retcode = unsafe { ffi::sqlite3_step(statement.stmt) };

        match retcode {
            ffi::SQLITE_ROW => Ok(Self::collect(statement, &mut 0)),
            ffi::SQLITE_DONE => Ok(None),
            other => Err(other.into()),
        }
    }
    fn columns_needed() -> c_int {
        T::columns_needed()
    }
}
impl Collectable for c_int {
    fn collect(statement: &Statement, column: &mut c_int) -> Self {
        let result = unsafe { ffi::sqlite3_column_int(statement.stmt, *column) };
        *column += 1;
        result
    }
    fn columns_needed() -> c_int {
        1
    }
}
impl Collectable for c_double {
    fn collect(statement: &Statement, column: &mut c_int) -> Self {
        let result = unsafe { ffi::sqlite3_column_double(statement.stmt, *column) };
        *column += 1;
        result
    }
    fn columns_needed() -> c_int {
        1
    }
}
impl Collectable for String {
    fn collect(statement: &Statement, column: &mut c_int) -> Self {
        let ptr = unsafe { ffi::sqlite3_column_text(statement.stmt, *column) };
        let bytes = unsafe { ffi::sqlite3_column_bytes(statement.stmt, *column) };

        *column += 1;

        match bytes == 0 {
            true => String::new(),
            false => unsafe {
                let slice = std::slice::from_raw_parts(ptr as *const u8, bytes as usize);
                String::from_utf8_unchecked(slice.to_owned())
            },
        }
    }
    fn columns_needed() -> c_int {
        1
    }
}
impl Collectable for Box<[u8]> {
    fn collect(statement: &Statement, column: &mut c_int) -> Self {
        let ptr = unsafe { ffi::sqlite3_column_blob(statement.stmt, *column) };
        let bytes = unsafe { ffi::sqlite3_column_bytes(statement.stmt, *column) };

        *column += 1;

        match bytes == 0 {
            true => Box::new([]),
            false => unsafe {
                let slice = std::slice::from_raw_parts(ptr as *const u8, bytes as usize);
                slice.to_owned().into_boxed_slice()
            },
        }
    }
    fn columns_needed() -> c_int {
        1
    }
}

macro_rules! collectable_tuple {
    ($columns_needed:expr, $($name: ident),+) => (
        impl<$($name),+> Collectable for ($($name,)+)
        where
            $($name: Collectable,)+
        {
            fn collect(statement: &Statement, column: &mut c_int) -> Self {
                (
                    $($name::collect(statement, column),)+
                )
            }
            fn columns_needed() -> c_int { $columns_needed }
        }
    );
}

collectable_tuple!(1, T0);
collectable_tuple!(2, T0, T1);
collectable_tuple!(3, T0, T1, T2);
collectable_tuple!(4, T0, T1, T2, T3);
collectable_tuple!(5, T0, T1, T2, T3, T4);
collectable_tuple!(6, T0, T1, T2, T3, T4, T5);
collectable_tuple!(7, T0, T1, T2, T3, T4, T5, T6);
collectable_tuple!(8, T0, T1, T2, T3, T4, T5, T6, T7);
collectable_tuple!(9, T0, T1, T2, T3, T4, T5, T6, T7, T8);
collectable_tuple!(10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
collectable_tuple!(11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
collectable_tuple!(12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
collectable_tuple!(13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);