From bde720bddc24bc75788e4386cb32363c060e0943 Mon Sep 17 00:00:00 2001 From: Ivan Ukhov Date: Mon, 8 Jun 2015 17:43:31 -0400 Subject: [PATCH] Refactor the error handling --- src/database.rs | 25 ++++++++++------------- src/error.rs | 39 +++++++++++++++++------------------- src/lib.rs | 16 ++++++++++----- src/statement.rs | 52 +++++++++++++++++++++++++++++------------------- tests/lib.rs | 8 ++++---- 5 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/database.rs b/src/database.rs index af1727a..95cc2e6 100644 --- a/src/database.rs +++ b/src/database.rs @@ -30,8 +30,8 @@ impl<'l> Database<'l> { #[inline] pub fn execute(&self, sql: &str) -> Result<()> { unsafe { - success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _, - 0 as *mut _)); + success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _, + 0 as *mut _)); } Ok(()) } @@ -46,10 +46,10 @@ impl<'l> Database<'l> { { unsafe { let callback = Box::new(callback); - success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), - Some(execute_callback::), - &*callback as *const _ as *mut _ as *mut _, - 0 as *mut _)); + success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), + Some(execute_callback::), + &*callback as *const _ as *mut _ as *mut _, + 0 as *mut _)); } Ok(()) } @@ -57,7 +57,7 @@ impl<'l> Database<'l> { /// Create a prepared statement. #[inline] pub fn prepare(&'l self, sql: &str) -> Result> { - ::statement::new(self, sql) + ::statement::new(self.raw, sql) } /// Set a callback for handling busy events. @@ -74,7 +74,7 @@ impl<'l> Database<'l> { let result = raw::sqlite3_busy_handler(self.raw, Some(busy_callback::), &*callback as *const _ as *mut _ as *mut _); self.busy_callback = Some(callback); - success!(result); + success!(self.raw, result); } Ok(()) } @@ -83,7 +83,7 @@ impl<'l> Database<'l> { /// rejected operations until a timeout expires. #[inline] pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> { - unsafe { success!(self, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) }; + unsafe { success!(self.raw, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) }; Ok(()) } @@ -91,7 +91,7 @@ impl<'l> Database<'l> { #[inline] pub fn remove_busy_handler(&mut self) -> Result<()> { ::std::mem::replace(&mut self.busy_callback, None); - unsafe { success!(raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)) }; + unsafe { success!(self.raw, raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)) }; Ok(()) } } @@ -105,11 +105,6 @@ impl<'l> Drop for Database<'l> { } } -#[inline] -pub fn as_raw(database: &Database) -> *mut raw::sqlite3 { - database.raw -} - extern fn busy_callback(callback: *mut c_void, attempts: c_int) -> c_int where F: FnMut(usize) -> bool { diff --git a/src/error.rs b/src/error.rs index 93661ea..03e26b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use raw; use std::convert::{From, Into}; use std::fmt::{self, Display, Formatter}; -use {Database, ResultCode}; +use ResultCode; /// An error. #[derive(Debug)] @@ -11,26 +11,6 @@ pub struct Error { pub message: Option, } -impl Error { - /// Return the last occurred error if any. - pub fn last(database: &Database) -> Option { - unsafe { - let code = raw::sqlite3_errcode(::database::as_raw(database)); - if code == raw::SQLITE_OK { - return None; - } - let message = raw::sqlite3_errmsg(::database::as_raw(database)); - if message.is_null() { - return None; - } - Some(Error { - code: ::result::code_from_raw(code), - message: Some(c_str_to_string!(message)), - }) - } - } -} - impl From for Error where T: Into { #[inline] fn from(message: T) -> Error { @@ -59,3 +39,20 @@ impl Display for Error { } } } + +pub fn last(raw: *mut raw::sqlite3) -> Option { + unsafe { + let code = raw::sqlite3_errcode(raw); + if code == raw::SQLITE_OK { + return None; + } + let message = raw::sqlite3_errmsg(raw); + if message.is_null() { + return None; + } + Some(Error { + code: ::result::code_from_raw(code), + message: Some(c_str_to_string!(message)), + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 6e7b645..960a787 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,14 +10,20 @@ macro_rules! raise( ($message:expr) => (return Err(::Error::from($message))); ); +macro_rules! failure( + ($database:expr, $code:expr) => ( + match ::error::last($database) { + Some(error) => return Err(error), + None => return Err(::Error::from(::result::code_from_raw($code))), + } + ); +); + macro_rules! success( ($database:expr, $result:expr) => ( match $result { ::raw::SQLITE_OK => {}, - code => match ::Error::last($database) { - Some(error) => return Err(error), - None => return Err(::Error::from(::result::code_from_raw(code))), - }, + code => failure!($database, code), } ); ($result:expr) => ( @@ -64,7 +70,7 @@ mod statement; pub use error::Error; pub use database::Database; pub use result::{Result, ResultCode}; -pub use statement::{Statement, Binding, Value}; +pub use statement::{Statement, Binding, Value, State}; /// Open a database. #[inline] diff --git a/src/statement.rs b/src/statement.rs index 4e03bae..fe0072a 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -2,12 +2,12 @@ use libc::{c_double, c_int}; use raw; use std::marker::PhantomData; -use {Database, Result, ResultCode}; +use Result; /// A prepared statement. pub struct Statement<'l> { - raw: *mut raw::sqlite3_stmt, - phantom: PhantomData<(&'l raw::sqlite3, raw::sqlite3_stmt)>, + raw: (*mut raw::sqlite3_stmt, *mut raw::sqlite3), + phantom: PhantomData<(raw::sqlite3_stmt, &'l raw::sqlite3)>, } /// A binding of a parameter of a prepared statement. @@ -23,6 +23,13 @@ pub trait Value { fn read(statement: &Statement, i: usize) -> Result; } +/// A state of a prepared statement. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum State { + Done, + Row, +} + impl<'l> Statement<'l> { /// Bind values to the parameters. /// @@ -32,17 +39,18 @@ impl<'l> Statement<'l> { match *binding { Binding::Float(i, value) => unsafe { debug_assert!(i > 0, "the indexing starts from 1"); - success!(raw::sqlite3_bind_double(self.raw, i as c_int, value as c_double)); + success!(self.raw.1, raw::sqlite3_bind_double(self.raw.0, i as c_int, + value as c_double)); }, Binding::Integer(i, value) => unsafe { debug_assert!(i > 0, "the indexing starts from 1"); - success!(raw::sqlite3_bind_int64(self.raw, i as c_int, - value as raw::sqlite3_int64)); + success!(self.raw.1, raw::sqlite3_bind_int64(self.raw.0, i as c_int, + value as raw::sqlite3_int64)); }, Binding::Text(i, value) => unsafe { debug_assert!(i > 0, "the indexing starts from 1"); - success!(raw::sqlite3_bind_text(self.raw, i as c_int, str_to_c_str!(value), -1, - None)); + success!(self.raw.1, raw::sqlite3_bind_text(self.raw.0, i as c_int, + str_to_c_str!(value), -1, None)); }, } } @@ -58,15 +66,18 @@ impl<'l> Statement<'l> { } /// Evaluate the statement. - #[inline] - pub fn step(&mut self) -> ResultCode { - unsafe { ::result::code_from_raw(raw::sqlite3_step(self.raw)) } + pub fn step(&mut self) -> Result { + match unsafe { raw::sqlite3_step(self.raw.0) } { + raw::SQLITE_DONE => Ok(State::Done), + raw::SQLITE_ROW => Ok(State::Row), + code => failure!(self.raw.1, code), + } } /// Reset the statement. #[inline] pub fn reset(&mut self) -> Result<()> { - unsafe { success!(raw::sqlite3_reset(self.raw)) }; + unsafe { success!(self.raw.1, raw::sqlite3_reset(self.raw.0)) }; Ok(()) } } @@ -74,26 +85,26 @@ impl<'l> Statement<'l> { impl<'l> Drop for Statement<'l> { #[inline] fn drop(&mut self) { - unsafe { ::raw::sqlite3_finalize(self.raw) }; + unsafe { raw::sqlite3_finalize(self.raw.0) }; } } impl Value for f64 { fn read(statement: &Statement, i: usize) -> Result { - Ok(unsafe { ::raw::sqlite3_column_double(statement.raw, i as c_int) as f64 }) + Ok(unsafe { raw::sqlite3_column_double(statement.raw.0, i as c_int) as f64 }) } } impl Value for i64 { fn read(statement: &Statement, i: usize) -> Result { - Ok(unsafe { ::raw::sqlite3_column_int64(statement.raw, i as c_int) as i64 }) + Ok(unsafe { raw::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 }) } } impl Value for String { fn read(statement: &Statement, i: usize) -> Result { unsafe { - let pointer = ::raw::sqlite3_column_text(statement.raw, i as c_int); + let pointer = raw::sqlite3_column_text(statement.raw.0, i as c_int); if pointer.is_null() { raise!("cannot read a TEXT column"); } @@ -103,11 +114,10 @@ impl Value for String { } #[inline] -pub fn new<'l>(database: &'l Database, sql: &str) -> Result> { - let mut raw = 0 as *mut _; +pub fn new<'l>(raw1: *mut raw::sqlite3, sql: &str) -> Result> { + let mut raw0 = 0 as *mut _; unsafe { - success!(database, raw::sqlite3_prepare(::database::as_raw(database), str_to_c_str!(sql), - -1, &mut raw, 0 as *mut _)); + success!(raw1, raw::sqlite3_prepare(raw1, str_to_c_str!(sql), -1, &mut raw0, 0 as *mut _)); } - Ok(Statement { raw: raw, phantom: PhantomData }) + Ok(Statement { raw: (raw0, raw1), phantom: PhantomData }) } diff --git a/tests/lib.rs b/tests/lib.rs index 7ec45ce..046278d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -11,7 +11,7 @@ macro_rules! ok( #[test] fn workflow() { use sqlite::Binding::*; - use sqlite::ResultCode; + use sqlite::State; macro_rules! pair( ($one:expr, $two:expr) => ((String::from($one), String::from($two))); @@ -27,7 +27,7 @@ fn workflow() { let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; let mut statement = ok!(database.prepare(sql)); ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); - assert!(statement.step() == ResultCode::Done); + assert!(ok!(statement.step()) == State::Done); } { @@ -47,11 +47,11 @@ fn workflow() { { let sql = r#"SELECT * FROM `users`;"#; let mut statement = ok!(database.prepare(sql)); - assert!(statement.step() == ResultCode::Row); + assert!(ok!(statement.step()) == State::Row); assert!(ok!(statement.column::(0)) == 1); assert!(ok!(statement.column::(1)) == String::from("Alice")); assert!(ok!(statement.column::(2)) == 20.99); - assert!(statement.step() == ResultCode::Done); + assert!(ok!(statement.step()) == State::Done); } }