diff --git a/src/chat.rs b/src/chat.rs index 221cc2e6f..a470739e6 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -64,13 +64,15 @@ impl Chat { ); match res { - Err(err @ crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => Err(err), + Err(err @ crate::sql::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => { + Err(err.into()) + } Err(err) => { error!( context, "chat: failed to load from db {}: {:?}", chat_id, err ); - Err(err) + Err(err.into()) } Ok(mut chat) => { match chat.id { @@ -129,7 +131,8 @@ impl Chat { &context.sql, "UPDATE chats SET param=? WHERE id=?", params![self.param.to_string(), self.id as i32], - ) + )?; + Ok(()) } pub fn get_id(&self) -> u32 { @@ -681,7 +684,7 @@ pub fn lookup_by_contact_id(context: &Context, contact_id: u32) -> Result<(u32, "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", params![contact_id as i32], |row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())), - ) + ).map_err(Into::into) } pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result { @@ -829,7 +832,8 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> { &context.sql, "UPDATE chats SET archived=0 WHERE id=?", params![chat_id as i32], - ) + )?; + Ok(()) } /// Send a message defined by a dc_msg_t object to a chat. @@ -976,7 +980,8 @@ fn do_set_draft(context: &Context, chat_id: u32, msg: &mut Message) -> Result<() msg.param.to_string(), 1, ], - ) + )?; + Ok(()) } // similar to as dc_set_draft() but does not emit an event @@ -1688,7 +1693,7 @@ pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Re context.sql.exists( "SELECT id FROM leftgrps WHERE grpid=?;", params![grpid.as_ref()], - ) + ).map_err(Into::into) } pub fn set_chat_name( diff --git a/src/config.rs b/src/config.rs index 759dc354a..0d6479811 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,6 @@ use crate::blob::BlobObject; use crate::constants::DC_VERSION_STR; use crate::context::Context; use crate::dc_tools::*; -use crate::error::Error; use crate::job::*; use crate::stock::StockMessage; @@ -113,7 +112,7 @@ impl Context { /// Set the given config key. /// If `None` is passed as a value the value is cleared and set to the default if there is one. - pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> { + pub fn set_config(&self, key: Config, value: Option<&str>) -> crate::sql::Result<()> { match key { Config::Selfavatar if value.is_some() => { let blob = BlobObject::create_from_path(&self, value.unwrap())?; diff --git a/src/contact.rs b/src/contact.rs index a2e85cb45..0f6bb20e8 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -141,7 +141,7 @@ pub enum VerifiedStatus { } impl Contact { - pub fn load_from_db(context: &Context, contact_id: u32) -> Result { + pub fn load_from_db(context: &Context, contact_id: u32) -> crate::sql::Result { if contact_id == DC_CONTACT_ID_SELF { let contact = Contact { id: contact_id, @@ -691,7 +691,7 @@ impl Contact { } Err(err) => { error!(context, "delete_contact {} failed ({})", contact_id, err); - return Err(err); + return Err(err.into()); } } } @@ -709,7 +709,7 @@ impl Contact { /// like "Me" in the selected language and the email address /// defined by dc_set_config(). pub fn get_by_id(context: &Context, contact_id: u32) -> Result { - Contact::load_from_db(context, contact_id) + Ok(Contact::load_from_db(context, contact_id)?) } /// Get the ID of the contact. diff --git a/src/error.rs b/src/error.rs index 45f8130f7..b1e8419d5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,18 +2,10 @@ use failure::Fail; #[derive(Debug, Fail)] pub enum Error { - #[fail(display = "Sqlite Error: {:?}", _0)] - Sql(rusqlite::Error), - #[fail(display = "Sqlite Connection Pool Error: {:?}", _0)] - ConnectionPool(r2d2::Error), #[fail(display = "{:?}", _0)] Failure(failure::Error), - #[fail(display = "Sqlite: Connection closed")] - SqlNoConnection, - #[fail(display = "Sqlite: Already open")] - SqlAlreadyOpen, - #[fail(display = "Sqlite: Failed to open")] - SqlFailedToOpen, + #[fail(display = "SQL error: {:?}", _0)] + SqlError(#[cause] crate::sql::Error), #[fail(display = "{:?}", _0)] Io(std::io::Error), #[fail(display = "{:?}", _0)] @@ -40,9 +32,9 @@ pub enum Error { pub type Result = std::result::Result; -impl From for Error { - fn from(err: rusqlite::Error) -> Error { - Error::Sql(err) +impl From for Error { + fn from(err: crate::sql::Error) -> Error { + Error::SqlError(err) } } @@ -58,12 +50,6 @@ impl From for Error { } } -impl From for Error { - fn from(err: r2d2::Error) -> Error { - Error::ConnectionPool(err) - } -} - impl From for Error { fn from(err: std::io::Error) -> Error { Error::Io(err) diff --git a/src/imex.rs b/src/imex.rs index 62e2b8021..816db76f1 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -462,7 +462,9 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef) -> Resul |files| { for (processed_files_cnt, file) in files.enumerate() { let (file_name, file_blob) = file?; - ensure!(!context.shall_stop_ongoing(), "received stop signal"); + if context.shall_stop_ongoing() { + return Ok(false); + } let mut permille = processed_files_cnt * 1000 / total_files_cnt; if permille < 10 { permille = 10 @@ -476,26 +478,25 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef) -> Resul } let path_filename = context.get_blobdir().join(file_name); - if dc_write_file(context, &path_filename, &file_blob).is_err() { - bail!( - "Storage full? Cannot write file {} with {} bytes.", - path_filename.display(), - file_blob.len(), - ); - } else { - continue; - } + dc_write_file(context, &path_filename, &file_blob)?; } - Ok(()) + Ok(true) }, ); - res.and_then(|_| { - // only delete backup_blobs if all files were successfully extracted - sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; - sql::try_execute(context, &context.sql, "VACUUM;").ok(); - Ok(()) - }) + match res { + Ok(all_files_extracted) => { + if all_files_extracted { + // only delete backup_blobs if all files were successfully extracted + sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; + sql::try_execute(context, &context.sql, "VACUUM;").ok(); + Ok(()) + } else { + bail!("received stop signal"); + } + } + Err(err) => Err(err.into()), + } } /******************************************************************************* @@ -553,7 +554,7 @@ fn export_backup(context: &Context, dir: impl AsRef) -> Result<()> { }; dest_sql.close(context); - res + Ok(res?) } fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { @@ -576,16 +577,15 @@ fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { info!(context, "EXPORT: total_files_cnt={}", total_files_cnt); // scan directory, pass 2: copy files let dir_handle = std::fs::read_dir(&dir)?; - sql.prepare( + let exported_all_files = sql.prepare( "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);", |mut stmt, _| { let mut processed_files_cnt = 0; for entry in dir_handle { let entry = entry?; - ensure!( - !context.shall_stop_ongoing(), - "canceled during export-files" - ); + if context.shall_stop_ongoing() { + return Ok(false); + } processed_files_cnt += 1; let permille = max(min(processed_files_cnt * 1000 / total_files_cnt, 990), 10); context.call_cb(Event::ImexProgress(permille)); @@ -605,9 +605,10 @@ fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { stmt.execute(params![name, buf])?; } } - Ok(()) + Ok(true) }, )?; + ensure!(exported_all_files, "canceled during export-files"); Ok(()) } diff --git a/src/location.rs b/src/location.rs index 92cb3a93a..aa421faa2 100644 --- a/src/location.rs +++ b/src/location.rs @@ -534,7 +534,7 @@ pub fn save( } Ok(newest_location_id) }, - ) + ).map_err(Into::into) } #[allow(non_snake_case)] diff --git a/src/message.rs b/src/message.rs index 916fdd98b..3904e37cc 100644 --- a/src/message.rs +++ b/src/message.rs @@ -266,7 +266,7 @@ impl Message { Ok(msg) }, - ) + ).map_err(Into::into) } pub fn delete_from_db(context: &Context, msg_id: MsgId) { @@ -1256,7 +1256,7 @@ pub(crate) fn rfc724_mid_exists( Ok((server_folder, server_uid, msg_id)) }, - ) + ).map_err(Into::into) } pub fn update_server_uid( diff --git a/src/peerstate.rs b/src/peerstate.rs index f26f1d88d..76a9d6774 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -8,7 +8,6 @@ use crate::aheader::*; use crate::chat::*; use crate::constants::*; use crate::context::Context; -use crate::error::*; use crate::key::*; use crate::sql::{self, Sql}; @@ -385,7 +384,7 @@ impl<'a> Peerstate<'a> { } } - pub fn save_to_db(&self, sql: &Sql, create: bool) -> Result<()> { + pub fn save_to_db(&self, sql: &Sql, create: bool) -> crate::sql::Result<()> { if create { sql::execute( self.context, diff --git a/src/sql.rs b/src/sql.rs index 744378c6b..6d6fb464c 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -1,5 +1,7 @@ //! # SQLite wrapper +use failure::Fail; + use std::collections::HashSet; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -11,10 +13,53 @@ use crate::chat::update_saved_messages_icon; use crate::constants::ShowEmails; use crate::context::Context; use crate::dc_tools::*; -use crate::error::{Error, Result}; use crate::param::*; use crate::peerstate::*; +#[derive(Debug, Fail)] +pub enum Error { + #[fail(display = "Sqlite Error: {:?}", _0)] + Sql(#[cause] rusqlite::Error), + #[fail(display = "Sqlite Connection Pool Error: {:?}", _0)] + ConnectionPool(#[cause] r2d2::Error), + #[fail(display = "Sqlite: Connection closed")] + SqlNoConnection, + #[fail(display = "Sqlite: Already open")] + SqlAlreadyOpen, + #[fail(display = "Sqlite: Failed to open")] + SqlFailedToOpen, + #[fail(display = "{:?}", _0)] + Io(#[cause] std::io::Error), + #[fail(display = "{:?}", _0)] + BlobError(#[cause] crate::blob::BlobError), +} + +pub type Result = std::result::Result; + +impl From for Error { + fn from(err: rusqlite::Error) -> Error { + Error::Sql(err) + } +} + +impl From for Error { + fn from(err: r2d2::Error) -> Error { + Error::ConnectionPool(err) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: crate::blob::BlobError) -> Error { + Error::BlobError(err) + } +} + /// A wrapper around the underlying Sqlite3 object. #[derive(DebugStub)] pub struct Sql { @@ -47,7 +92,7 @@ impl Sql { pub fn open(&self, context: &Context, dbfile: &std::path::Path, readonly: bool) -> bool { match open(context, self, dbfile, readonly) { Ok(_) => true, - Err(Error::SqlAlreadyOpen) => false, + Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false, Err(_) => { self.close(context); false @@ -321,14 +366,14 @@ fn open( sql: &Sql, dbfile: impl AsRef, readonly: bool, -) -> Result<()> { +) -> crate::error::Result<()> { if sql.is_open() { error!( context, "Cannot open, database \"{:?}\" already opened.", dbfile.as_ref(), ); - return Err(Error::SqlAlreadyOpen); + return Err(Error::SqlAlreadyOpen.into()); } let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX; @@ -345,7 +390,8 @@ fn open( .min_idle(Some(2)) .max_size(10) .connection_timeout(std::time::Duration::new(60, 0)) - .build(mgr)?; + .build(mgr) + .map_err(Error::ConnectionPool)?; { *sql.pool.write().unwrap() = Some(pool); @@ -477,7 +523,7 @@ fn open( dbfile.as_ref(), ); // cannot create the tables - maybe we cannot write? - return Err(Error::SqlFailedToOpen); + return Err(Error::SqlFailedToOpen.into()); } else { sql.set_raw_config_int(context, "dbversion", 0)?; }