Add ToSql/FromSql for time::Timespec.

Uses SQLite's default format string for `datetime()`.
This commit is contained in:
John Gallagher 2014-10-22 22:16:00 -04:00
parent 1fa4c2098e
commit c8af7e4eb5

View File

@ -1,3 +1,5 @@
extern crate time;
use libc::{c_int, c_double}; use libc::{c_int, c_double};
use std::c_str::{CString}; use std::c_str::{CString};
use std::mem; use std::mem;
@ -5,6 +7,8 @@ use std::vec;
use super::ffi; use super::ffi;
use super::{SqliteResult, SqliteError}; use super::{SqliteResult, SqliteError};
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
pub trait ToSql { pub trait ToSql {
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int; unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int;
} }
@ -55,6 +59,13 @@ impl ToSql for Vec<u8> {
} }
} }
impl ToSql for time::Timespec {
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int {
let time_str = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap();
time_str.bind_parameter(stmt, col)
}
}
impl<T: ToSql> ToSql for Option<T> { impl<T: ToSql> ToSql for Option<T> {
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int { unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int {
match *self { match *self {
@ -112,6 +123,20 @@ impl FromSql for Vec<u8> {
} }
} }
impl FromSql for time::Timespec {
unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt,
col: c_int) -> SqliteResult<time::Timespec> {
let col_str = FromSql::column_result(stmt, col);
col_str.and_then(|txt: String| {
time::strptime(txt.as_slice(), SQLITE_DATETIME_FMT).map(|tm| {
tm.to_timespec()
}).map_err(|parse_error| {
SqliteError{ code: ffi::SQLITE_MISMATCH, message: format!("{}", parse_error) }
})
})
}
}
impl<T: FromSql> FromSql for Option<T> { impl<T: FromSql> FromSql for Option<T> {
unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> SqliteResult<Option<T>> { unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> SqliteResult<Option<T>> {
if ffi::sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL { if ffi::sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL {
@ -125,6 +150,7 @@ impl<T: FromSql> FromSql for Option<T> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use SqliteConnection; use SqliteConnection;
use super::time;
fn checked_memory_handle() -> SqliteConnection { fn checked_memory_handle() -> SqliteConnection {
let db = SqliteConnection::open(":memory:").unwrap(); let db = SqliteConnection::open(":memory:").unwrap();
@ -154,6 +180,17 @@ mod test {
assert_eq!(from.as_slice(), s); assert_eq!(from.as_slice(), s);
} }
#[test]
fn test_timespec() {
let db = checked_memory_handle();
let ts = time::Timespec{sec: 10_000, nsec: 0 };
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
let from: time::Timespec = db.query_row("SELECT t FROM foo", [], |r| r.unwrap().get(0));
assert_eq!(from, ts);
}
#[test] #[test]
fn test_option() { fn test_option() {
let db = checked_memory_handle(); let db = checked_memory_handle();