From 06d59f59a52f6934219b100ce17e4d90ac6d0d5d Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 3 Mar 2018 22:43:28 +0100 Subject: [PATCH] Refactor rust errors (#722) Refactor rust errors --- Makefile | 8 +- rust/.gitignore | 1 - rust/Cargo.lock | 45 ++++++- rust/Cargo.toml | 6 +- rust/build.rs | 161 -------------------------- rust/cbindgen.toml | 4 + rust/src/errors.rs | 74 ++++++++++++ rust/src/item.rs | 44 ++++--- rust/src/lib.rs | 101 ++-------------- rust/src/storage/exports.rs | 193 +++++++++++++++++++++++++++++++ rust/src/storage/mod.rs | 140 ++-------------------- rust/src/storage/singlefile.rs | 134 ++++++++++++--------- rust/vdirsyncer_rustext.h | 90 ++++++++++++++ setup.py | 8 +- vdirsyncer/native.py | 33 +++--- vdirsyncer/storage/_rust.py | 15 ++- vdirsyncer/storage/singlefile.py | 2 +- vdirsyncer/vobject.py | 4 +- 18 files changed, 561 insertions(+), 502 deletions(-) delete mode 100644 rust/build.rs create mode 100644 rust/cbindgen.toml create mode 100644 rust/src/errors.rs create mode 100644 rust/src/storage/exports.rs create mode 100644 rust/vdirsyncer_rustext.h diff --git a/Makefile b/Makefile index 45d6eb6..1f7aee0 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,8 @@ install-dev: pip install -U --force-reinstall \ git+https://github.com/mitsuhiko/click \ git+https://github.com/click-contrib/click-log \ - git+https://github.com/kennethreitz/requests; \ + git+https://github.com/kennethreitz/requests \ + "git+https://github.com/untitaker/shippai#subdirectory=python"; \ elif [ "$(REQUIREMENTS)" = "minimal" ]; then \ pip install -U --force-reinstall $$(python setup.py --quiet minimal_requirements); \ fi @@ -152,4 +153,7 @@ rust-ext: [ "$$READTHEDOCS" != "True" ] || $(MAKE) install-rust cd ./rust && cargo build --release -.PHONY: docs +rust/vdirsyncer_rustext.h: + cbindgen -c rust/cbindgen.toml rust/ > $@ + +.PHONY: docs rust/vdirsyncer_rustext.h diff --git a/rust/.gitignore b/rust/.gitignore index ac3376e..2f7896d 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1,2 +1 @@ target/ -src/storage/exports.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 35d50a1..6a74333 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -120,6 +120,25 @@ dependencies = [ "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -310,6 +329,14 @@ dependencies = [ "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "shippai" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.6.0" @@ -333,6 +360,15 @@ dependencies = [ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tempdir" version = "0.3.5" @@ -383,13 +419,14 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "vdirsyncer_rustext" +name = "vdirsyncer-rustext" version = "0.1.0" dependencies = [ "atomicwrites 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "cbindgen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "shippai 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "vobject 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -456,6 +493,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" +"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" @@ -481,9 +520,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" "checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" "checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" +"checksum shippai 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c342d76b09cc95857d435bb96f03c1eac8218c819e799adcc1225bdc7fe48ede" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index fbfe57a..25d6cc2 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,8 +1,7 @@ [package] -name = "vdirsyncer_rustext" +name = "vdirsyncer-rustext" version = "0.1.0" authors = ["Markus Unterwaditzer "] -build = "build.rs" [lib] name = "vdirsyncer_rustext" @@ -11,7 +10,8 @@ crate-type = ["cdylib"] [dependencies] vobject = "0.4.2" ring = "0.12.1" -error-chain = "0.11.0" +failure = "0.1" +shippai = "0.1.1" atomicwrites = "0.1.4" [build-dependencies] diff --git a/rust/build.rs b/rust/build.rs deleted file mode 100644 index 143c8d1..0000000 --- a/rust/build.rs +++ /dev/null @@ -1,161 +0,0 @@ -extern crate cbindgen; - -use std::env; -use std::fs::{remove_file, File}; -use std::io::Write; -use std::path::Path; - -const TEMPLATE_EACH: &'static str = r#" -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_list( - storage: *mut {path}, - err: *mut VdirsyncerError -) -> *mut VdirsyncerStorageListing { - match (*storage).list() { - Ok(x) => Box::into_raw(Box::new(VdirsyncerStorageListing { - iterator: x, - href: None, - etag: None - })), - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_get( - storage: *mut {path}, - c_href: *const c_char, - err: *mut VdirsyncerError -) -> *mut VdirsyncerStorageGetResult { - let href = CStr::from_ptr(c_href); - match (*storage).get(href.to_str().unwrap()) { - Ok((item, href)) => { - Box::into_raw(Box::new(VdirsyncerStorageGetResult { - item: Box::into_raw(Box::new(item)), - etag: CString::new(href).unwrap().into_raw() - })) - }, - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_upload( - storage: *mut {path}, - item: *mut Item, - err: *mut VdirsyncerError -) -> *mut VdirsyncerStorageUploadResult { - match (*storage).upload((*item).clone()) { - Ok((href, etag)) => { - Box::into_raw(Box::new(VdirsyncerStorageUploadResult { - href: CString::new(href).unwrap().into_raw(), - etag: CString::new(etag).unwrap().into_raw() - })) - }, - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_update( - storage: *mut {path}, - c_href: *const c_char, - item: *mut Item, - c_etag: *const c_char, - err: *mut VdirsyncerError -) -> *const c_char { - let href = CStr::from_ptr(c_href); - let etag = CStr::from_ptr(c_etag); - match (*storage).update(href.to_str().unwrap(), (*item).clone(), etag.to_str().unwrap()) { - Ok(etag) => CString::new(etag).unwrap().into_raw(), - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } - - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_delete( - storage: *mut {path}, - c_href: *const c_char, - c_etag: *const c_char, - err: *mut VdirsyncerError -) { - let href = CStr::from_ptr(c_href); - let etag = CStr::from_ptr(c_etag); - match (*storage).delete(href.to_str().unwrap(), etag.to_str().unwrap()) { - Ok(()) => (), - Err(e) => e.fill_c_err(err) - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_buffered(storage: *mut {path}) { - (*storage).buffered(); -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_{name}_flush( - storage: *mut {path}, - err: *mut VdirsyncerError -) { - match (*storage).flush() { - Ok(_) => (), - Err(e) => e.fill_c_err(err) - } -} -"#; - -fn export_storage(f: &mut File, name: &str, path: &str) { - // String formatting in rust is at compile time. That doesn't work well for our case. - write!( - f, - "{}", - TEMPLATE_EACH - .replace("{name}", name) - .replace("{path}", path) - ).unwrap(); -} - -fn main() { - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - - let mut f = File::create(Path::new(&crate_dir).join("src/storage/exports.rs")).unwrap(); - write!(f, "// Auto-generated, do not check in.\n").unwrap(); - write!(f, "use std::os::raw::c_char;\n").unwrap(); - write!(f, "use std::mem;\n").unwrap(); - write!(f, "use std::ffi::{{CStr, CString}};\n").unwrap(); - write!(f, "use errors::*;\n").unwrap(); - write!(f, "use item::Item;\n").unwrap(); - write!(f, "use super::VdirsyncerStorageListing;\n").unwrap(); - write!(f, "use super::VdirsyncerStorageGetResult;\n").unwrap(); - write!(f, "use super::VdirsyncerStorageUploadResult;\n").unwrap(); - write!(f, "use super::Storage;\n").unwrap(); - - write!(f, "use super::singlefile;\n").unwrap(); - export_storage(&mut f, "singlefile", "singlefile::SinglefileStorage"); - drop(f); - - let _ = remove_file(Path::new(&crate_dir).join("target/vdirsyncer_rustext.h")); - - let res = cbindgen::Builder::new() - .with_crate(crate_dir) - .with_language(cbindgen::Language::C) - .generate(); - - match res { - Ok(x) => x.write_to_file("target/vdirsyncer_rustext.h"), - Err(e) => println!("FAILED TO GENERATE BINDINGS: {:?}", e), - } -} diff --git a/rust/cbindgen.toml b/rust/cbindgen.toml new file mode 100644 index 0000000..bd401bf --- /dev/null +++ b/rust/cbindgen.toml @@ -0,0 +1,4 @@ +language = "C" + +[parse] +expand = ["vdirsyncer-rustext"] diff --git a/rust/src/errors.rs b/rust/src/errors.rs new file mode 100644 index 0000000..25b695e --- /dev/null +++ b/rust/src/errors.rs @@ -0,0 +1,74 @@ +use failure; + +pub type Fallible = Result; + +#[derive(Debug, Fail)] +#[fail(display = "The item cannot be parsed")] +pub struct ItemUnparseable; + +#[derive(Debug, Fail)] +#[fail(display = "Unexpected version {}, expected {}", found, expected)] +pub struct UnexpectedVobjectVersion { + pub found: String, + pub expected: String, +} + +#[derive(Debug, Fail)] +#[fail(display = "Unexpected component {}, expected {}", found, expected)] +pub struct UnexpectedVobject { + pub found: String, + pub expected: String, +} + +#[derive(Debug, Fail)] +#[fail(display = "Item '{}' not found", href)] +pub struct ItemNotFound { + pub href: String, +} + +#[derive(Debug, Fail)] +#[fail(display = "The href '{}' is already taken", href)] +pub struct ItemAlreadyExisting { + pub href: String, +} + +#[derive(Debug, Fail)] +#[fail(display = "A wrong etag for '{}' was provided. Another client's requests might \ + conflict with vdirsyncer.", + href)] +pub struct WrongEtag { + pub href: String, +} + +#[derive(Debug, Fail)] +#[fail(display = "The mtime for '{}' has unexpectedly changed. Please close other programs\ + accessing this file.", + filepath)] +pub struct MtimeMismatch { + pub filepath: String, +} + +pub unsafe fn export_result( + res: Result, + c_err: *mut *mut exports::ShippaiError, +) -> Option { + match res { + Ok(v) => Some(v), + Err(e) => { + *c_err = Box::into_raw(Box::new(e.into())); + None + } + } +} + +pub mod exports { + shippai_export! { + super::ItemUnparseable as ITEM_UNPARSEABLE, + super::UnexpectedVobjectVersion as UNEXPECTED_VOBJECT_VERSION, + super::UnexpectedVobject as UNEXPECTED_VOBJECT, + super::ItemNotFound as ITEM_NOT_FOUND, + super::ItemAlreadyExisting as ITEM_ALREADY_EXISTING, + super::WrongEtag as WRONG_ETAG, + super::MtimeMismatch as MTIME_MISMATCH + } +} diff --git a/rust/src/item.rs b/rust/src/item.rs index 99697c1..08dba7f 100644 --- a/rust/src/item.rs +++ b/rust/src/item.rs @@ -41,13 +41,13 @@ impl Item { None } - pub fn with_uid(&self, uid: &str) -> Result { + pub fn with_uid(&self, uid: &str) -> Fallible { if let Item::Parsed(ref component) = *self { let mut new_component = component.clone(); change_uid(&mut new_component, uid); Ok(Item::from_raw(vobject::write_component(&new_component))) } else { - Err(ErrorKind::ItemUnparseable.into()) + Err(ItemUnparseable.into()) } } @@ -60,34 +60,34 @@ impl Item { } /// Component of item if parseable - pub fn get_component(&self) -> Result<&vobject::Component> { + pub fn get_component(&self) -> Fallible<&vobject::Component> { match *self { Item::Parsed(ref component) => Ok(component), - _ => Err(ErrorKind::ItemUnparseable.into()), + _ => Err(ItemUnparseable.into()), } } /// Component of item if parseable - pub fn into_component(self) -> Result { + pub fn into_component(self) -> Fallible { match self { Item::Parsed(component) => Ok(component), - _ => Err(ErrorKind::ItemUnparseable.into()), + _ => Err(ItemUnparseable.into()), } } /// Used for etags - pub fn get_hash(&self) -> Result { + pub fn get_hash(&self) -> Fallible { // FIXME: cache if let Item::Parsed(ref component) = *self { Ok(hash_component(component)) } else { - Err(ErrorKind::ItemUnparseable.into()) + Err(ItemUnparseable.into()) } } /// Used for generating hrefs and matching up items during synchronization. This is either the /// UID or the hash of the item's content. - pub fn get_ident(&self) -> Result { + pub fn get_ident(&self) -> Fallible { if let Some(x) = self.get_uid() { return Ok(x); } @@ -188,7 +188,7 @@ fn hash_component(c: &vobject::Component) -> String { pub mod exports { use super::Item; - use std::mem; + use std::ptr; use std::ffi::{CStr, CString}; use std::os::raw::c_char; use errors::*; @@ -225,29 +225,25 @@ pub mod exports { pub unsafe extern "C" fn vdirsyncer_with_uid( c: *mut Item, uid: *const c_char, - err: *mut VdirsyncerError, + err: *mut *mut exports::ShippaiError, ) -> *mut Item { let uid_cstring = CStr::from_ptr(uid); - match (*c).with_uid(uid_cstring.to_str().unwrap()) { - Ok(x) => Box::into_raw(Box::new(x)), - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } + if let Some(x) = export_result((*c).with_uid(uid_cstring.to_str().unwrap()), err) { + Box::into_raw(Box::new(x)) + } else { + ptr::null_mut() } } #[no_mangle] pub unsafe extern "C" fn vdirsyncer_get_hash( c: *mut Item, - err: *mut VdirsyncerError, + err: *mut *mut exports::ShippaiError, ) -> *const c_char { - match (*c).get_hash() { - Ok(x) => CString::new(x).unwrap().into_raw(), - Err(e) => { - e.fill_c_err(err); - mem::zeroed() - } + if let Some(x) = export_result((*c).get_hash(), err) { + CString::new(x).unwrap().into_raw() + } else { + ptr::null_mut() } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index dcd30ea..0272a5f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,104 +1,25 @@ extern crate atomicwrites; #[macro_use] -extern crate error_chain; +extern crate failure; +#[macro_use] +extern crate shippai; extern crate ring; extern crate vobject; -pub mod item; -pub mod storage; - -mod errors { - use std::ffi::CString; - use std::os::raw::c_char; - use vobject; - use atomicwrites; - - error_chain!{ - links { - Vobject(vobject::error::VObjectError, vobject::error::VObjectErrorKind); - } - - foreign_links { - Io(::std::io::Error); - } - - errors { - ItemUnparseable { - description("ItemUnparseable: The item cannot be parsed."), - display("The item cannot be parsed."), - } - - VobjectVersionMismatch(first: String, second: String) { - description("Incompatible vobject versions."), - display("Conflict between {} and {}", first, second), - } - - UnexpectedVobject(found: String, expected: String) { - description("Unexpected component type"), - display("Found type {}, expected {}", found, expected), - } - - ItemNotFound(href: String) { - description("ItemNotFound: The item could not be found"), - display("The item '{}' could not be found", href), - } - - AlreadyExisting(href: String) { - description("AlreadyExisting: An item at this href already exists"), - display("The href '{}' is already taken", href), - } - - WrongEtag(href: String) { - description("WrongEtag: A wrong etag was provided."), - display("A wrong etag for '{}' was provided. This indicates that two clients are writing data at the same time.", href), - } - - MtimeMismatch(filepath: String) { - description("MtimeMismatch: Two programs access the same file."), - display("The mtime of {} has unexpectedly changed. Please close other programs accessing this file.", filepath), - } - } - } - - impl From> for Error { - fn from(e: atomicwrites::Error) -> Error { - match e { - atomicwrites::Error::Internal(x) => x.into(), - atomicwrites::Error::User(x) => x, - } - } - } - - pub trait ErrorExt: ::std::error::Error { - unsafe fn fill_c_err(&self, err: *mut VdirsyncerError) { - (*err).failed = true; - (*err).msg = CString::new(self.description()).unwrap().into_raw(); - } - } - - impl ErrorExt for Error {} - - #[repr(C)] - pub struct VdirsyncerError { - pub failed: bool, - pub msg: *mut c_char, - } -} +mod item; +mod storage; +mod errors; pub mod exports { - use std::ffi::{CStr, CString}; - use std::ptr; + use std::ffi::CStr; use std::os::raw::c_char; - use errors::*; + + pub use super::item::exports::*; + pub use super::storage::exports::*; + pub use super::errors::exports::*; #[no_mangle] pub unsafe extern "C" fn vdirsyncer_free_str(s: *const c_char) { CStr::from_ptr(s); } - - #[no_mangle] - pub unsafe extern "C" fn vdirsyncer_clear_err(e: *mut VdirsyncerError) { - CString::from_raw((*e).msg); - (*e).msg = ptr::null_mut(); - } } diff --git a/rust/src/storage/exports.rs b/rust/src/storage/exports.rs new file mode 100644 index 0000000..d696c03 --- /dev/null +++ b/rust/src/storage/exports.rs @@ -0,0 +1,193 @@ +use std::os::raw::c_char; +use std::ptr; +use std::ffi::{CStr, CString}; +use errors::*; +use item::Item; +use super::Storage; +pub use super::singlefile::exports::*; + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_free(storage: *mut Box) { + let _: Box> = Box::from_raw(storage); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_list( + storage: *mut Box, + err: *mut *mut exports::ShippaiError, +) -> *mut VdirsyncerStorageListing { + if let Some(x) = export_result((**storage).list(), err) { + Box::into_raw(Box::new(VdirsyncerStorageListing { + iterator: x, + href: None, + etag: None, + })) + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_get( + storage: *mut Box, + c_href: *const c_char, + err: *mut *mut exports::ShippaiError, +) -> *mut VdirsyncerStorageGetResult { + let href = CStr::from_ptr(c_href); + if let Some((item, href)) = export_result((**storage).get(href.to_str().unwrap()), err) { + Box::into_raw(Box::new(VdirsyncerStorageGetResult { + item: Box::into_raw(Box::new(item)), + etag: CString::new(href).unwrap().into_raw(), + })) + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_upload( + storage: *mut Box, + item: *mut Item, + err: *mut *mut exports::ShippaiError, +) -> *mut VdirsyncerStorageUploadResult { + if let Some((href, etag)) = export_result((**storage).upload((*item).clone()), err) { + Box::into_raw(Box::new(VdirsyncerStorageUploadResult { + href: CString::new(href).unwrap().into_raw(), + etag: CString::new(etag).unwrap().into_raw(), + })) + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_update( + storage: *mut Box, + c_href: *const c_char, + item: *mut Item, + c_etag: *const c_char, + err: *mut *mut exports::ShippaiError, +) -> *const c_char { + let href = CStr::from_ptr(c_href); + let etag = CStr::from_ptr(c_etag); + let res = (**storage).update( + href.to_str().unwrap(), + (*item).clone(), + etag.to_str().unwrap(), + ); + if let Some(etag) = export_result(res, err) { + CString::new(etag).unwrap().into_raw() + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_delete( + storage: *mut Box, + c_href: *const c_char, + c_etag: *const c_char, + err: *mut *mut exports::ShippaiError, +) { + let href = CStr::from_ptr(c_href); + let etag = CStr::from_ptr(c_etag); + let res = (**storage).delete(href.to_str().unwrap(), etag.to_str().unwrap()); + let _ = export_result(res, err); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_buffered(storage: *mut Box) { + (**storage).buffered(); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_flush( + storage: *mut Box, + err: *mut *mut exports::ShippaiError, +) { + let _ = export_result((**storage).flush(), err); +} + +pub struct VdirsyncerStorageListing { + iterator: Box>, + href: Option, + etag: Option, +} + +impl VdirsyncerStorageListing { + pub fn advance(&mut self) -> bool { + match self.iterator.next() { + Some((href, etag)) => { + self.href = Some(href); + self.etag = Some(etag); + true + } + None => { + self.href = None; + self.etag = None; + false + } + } + } + + pub fn get_href(&mut self) -> Option { + self.href.take() + } + pub fn get_etag(&mut self) -> Option { + self.etag.take() + } +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_free_storage_listing(listing: *mut VdirsyncerStorageListing) { + let _: Box = Box::from_raw(listing); +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_advance_storage_listing( + listing: *mut VdirsyncerStorageListing, +) -> bool { + (*listing).advance() +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_listing_get_href( + listing: *mut VdirsyncerStorageListing, +) -> *const c_char { + CString::new((*listing).get_href().unwrap()) + .unwrap() + .into_raw() +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_storage_listing_get_etag( + listing: *mut VdirsyncerStorageListing, +) -> *const c_char { + CString::new((*listing).get_etag().unwrap()) + .unwrap() + .into_raw() +} + +#[repr(C)] +pub struct VdirsyncerStorageGetResult { + pub item: *mut Item, + pub etag: *const c_char, +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_free_storage_get_result(res: *mut VdirsyncerStorageGetResult) { + let _: Box = Box::from_raw(res); +} + +#[repr(C)] +pub struct VdirsyncerStorageUploadResult { + pub href: *const c_char, + pub etag: *const c_char, +} + +#[no_mangle] +pub unsafe extern "C" fn vdirsyncer_free_storage_upload_result( + res: *mut VdirsyncerStorageUploadResult, +) { + let _: Box = Box::from_raw(res); +} diff --git a/rust/src/storage/mod.rs b/rust/src/storage/mod.rs index bbe3b3d..86ee176 100644 --- a/rust/src/storage/mod.rs +++ b/rust/src/storage/mod.rs @@ -1,40 +1,20 @@ pub mod singlefile; pub mod exports; -use std::ffi::CString; -use std::os::raw::c_char; -use errors::*; +use errors::Fallible; use item::Item; type ItemAndEtag = (Item, String); -pub trait Storage: Sized { +pub trait Storage { /// returns an iterator of `(href, etag)` - fn list<'a>(&'a mut self) -> Result + 'a>>; + fn list<'a>(&'a mut self) -> Fallible + 'a>>; ///Fetch a single item. /// ///:param href: href to fetch ///:returns: (item, etag) ///:raises: :exc:`vdirsyncer.exceptions.PreconditionFailed` if item can't be found. - fn get(&mut self, href: &str) -> Result; - - /// Fetch multiple items. Duplicate hrefs must be ignored. - /// - /// Functionally similar to `get`, but might bring performance benefits on some storages when - /// used cleverly. - /// - /// # Parameters - /// - `hrefs`: list of hrefs to fetch - /// - returns an iterator of `(href, item, etag)` - fn get_multi<'a, I: Iterator + 'a>( - &'a mut self, - hrefs: I, - ) -> Box)> + 'a> { - Box::new(DefaultGetMultiIterator { - storage: self, - href_iter: hrefs, - }) - } + fn get(&mut self, href: &str) -> Fallible; /// Upload a new item. /// @@ -43,17 +23,17 @@ pub trait Storage: Sized { /// special case only exists because of DAV. Avoid this situation whenever possible. /// /// Returns `(href, etag)` - fn upload(&mut self, item: Item) -> Result<(String, String)>; + fn upload(&mut self, item: Item) -> Fallible<(String, String)>; /// Update an item. /// /// The etag may be none in some cases, see `upload`. /// /// Returns `etag` - fn update(&mut self, href: &str, item: Item, etag: &str) -> Result; + fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible; /// Delete an item by href. - fn delete(&mut self, href: &str, etag: &str) -> Result<()>; + fn delete(&mut self, href: &str, etag: &str) -> Fallible<()>; /// Enter buffered mode for storages that support it. /// @@ -64,111 +44,7 @@ pub trait Storage: Sized { fn buffered(&mut self) {} /// Write back all changes to the collection. - fn flush(&mut self) -> Result<()> { + fn flush(&mut self) -> Fallible<()> { Ok(()) } } - -struct DefaultGetMultiIterator<'a, S: Storage + 'a, I: Iterator> { - storage: &'a mut S, - href_iter: I, -} - -impl<'a, S, I> Iterator for DefaultGetMultiIterator<'a, S, I> -where - S: Storage, - I: Iterator, -{ - type Item = (String, Result); - - fn next(&mut self) -> Option { - match self.href_iter.next() { - Some(x) => Some((x.to_owned(), self.storage.get(&x))), - None => None, - } - } -} - -pub struct VdirsyncerStorageListing { - iterator: Box>, - href: Option, - etag: Option, -} - -impl VdirsyncerStorageListing { - pub fn advance(&mut self) -> bool { - match self.iterator.next() { - Some((href, etag)) => { - self.href = Some(href); - self.etag = Some(etag); - true - } - None => { - self.href = None; - self.etag = None; - false - } - } - } - - pub fn get_href(&mut self) -> Option { - self.href.take() - } - pub fn get_etag(&mut self) -> Option { - self.etag.take() - } -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_free_storage_listing(listing: *mut VdirsyncerStorageListing) { - let _: Box = Box::from_raw(listing); -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_advance_storage_listing( - listing: *mut VdirsyncerStorageListing, -) -> bool { - (*listing).advance() -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_storage_listing_get_href( - listing: *mut VdirsyncerStorageListing, -) -> *const c_char { - CString::new((*listing).get_href().unwrap()) - .unwrap() - .into_raw() -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_storage_listing_get_etag( - listing: *mut VdirsyncerStorageListing, -) -> *const c_char { - CString::new((*listing).get_etag().unwrap()) - .unwrap() - .into_raw() -} - -#[repr(C)] -pub struct VdirsyncerStorageGetResult { - pub item: *mut Item, - pub etag: *const c_char, -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_free_storage_get_result(res: *mut VdirsyncerStorageGetResult) { - let _: Box = Box::from_raw(res); -} - -#[repr(C)] -pub struct VdirsyncerStorageUploadResult { - pub href: *const c_char, - pub etag: *const c_char, -} - -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_free_storage_upload_result( - res: *mut VdirsyncerStorageUploadResult, -) { - let _: Box = Box::from_raw(res); -} diff --git a/rust/src/storage/singlefile.rs b/rust/src/storage/singlefile.rs index a421c6a..bf71a60 100644 --- a/rust/src/storage/singlefile.rs +++ b/rust/src/storage/singlefile.rs @@ -1,5 +1,3 @@ -use std::ffi::CStr; -use std::os::raw::c_char; use std::path::{Path, PathBuf}; use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry::*; @@ -34,14 +32,14 @@ impl SinglefileStorage { } } - fn get_items(&mut self) -> Result<&mut ItemCache> { + fn get_items(&mut self) -> Fallible<&mut ItemCache> { if self.items_cache.is_none() { self.list()?; } Ok(&mut self.items_cache.as_mut().unwrap().0) } - fn write_back(&mut self) -> Result<()> { + fn write_back(&mut self) -> Fallible<()> { self.dirty_cache = true; if self.buffered_mode { return Ok(()); @@ -52,19 +50,22 @@ impl SinglefileStorage { } } -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_init_singlefile(path: *const c_char) -> *mut SinglefileStorage { - let cstring = CStr::from_ptr(path); - Box::into_raw(Box::new(SinglefileStorage::new(cstring.to_str().unwrap()))) -} +pub mod exports { + use super::*; + use std::ffi::CStr; + use std::os::raw::c_char; -#[no_mangle] -pub unsafe extern "C" fn vdirsyncer_free_singlefile(s: *mut SinglefileStorage) { - let _: Box = Box::from_raw(s); + #[no_mangle] + pub unsafe extern "C" fn vdirsyncer_init_singlefile(path: *const c_char) -> *mut Box { + let cstring = CStr::from_ptr(path); + Box::into_raw(Box::new(Box::new(SinglefileStorage::new( + cstring.to_str().unwrap(), + )))) + } } impl Storage for SinglefileStorage { - fn list<'a>(&'a mut self) -> Result + 'a>> { + fn list<'a>(&'a mut self) -> Fallible + 'a>> { let mut new_cache = BTreeMap::new(); let mtime = metadata(&self.path)?.modified()?; let mut f = File::open(&self.path)?; @@ -84,51 +85,61 @@ impl Storage for SinglefileStorage { ))) } - fn get(&mut self, href: &str) -> Result<(Item, String)> { + fn get(&mut self, href: &str) -> Fallible<(Item, String)> { match self.get_items()?.get(href) { Some(&(ref href, ref etag)) => Ok((href.clone(), etag.clone())), - None => Err(ErrorKind::ItemNotFound(href.to_owned()))?, + None => Err(ItemNotFound { + href: href.to_owned(), + })?, } } - fn upload(&mut self, item: Item) -> Result<(String, String)> { + fn upload(&mut self, item: Item) -> Fallible<(String, String)> { let hash = item.get_hash()?; let href = item.get_ident()?; match self.get_items()?.entry(href.clone()) { - Occupied(_) => Err(ErrorKind::AlreadyExisting(href.clone()))?, + Occupied(_) => Err(ItemAlreadyExisting { href: href.clone() })?, Vacant(vc) => vc.insert((item, hash.clone())), }; self.write_back()?; Ok((href, hash)) } - fn update(&mut self, href: &str, item: Item, etag: &str) -> Result { + fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible { let hash = match self.get_items()?.entry(href.to_owned()) { Occupied(mut oc) => { if oc.get().1 == etag { let hash = item.get_hash()?; oc.insert((item, hash.clone())); - Ok(hash) + hash } else { - Err(ErrorKind::WrongEtag(href.to_owned())) + Err(WrongEtag { + href: href.to_owned(), + })? } } - Vacant(_) => Err(ErrorKind::ItemNotFound(href.to_owned())), - }?; + Vacant(_) => Err(ItemNotFound { + href: href.to_owned(), + })?, + }; self.write_back()?; Ok(hash) } - fn delete(&mut self, href: &str, etag: &str) -> Result<()> { + fn delete(&mut self, href: &str, etag: &str) -> Fallible<()> { match self.get_items()?.entry(href.to_owned()) { Occupied(oc) => { if oc.get().1 == etag { oc.remove(); } else { - Err(ErrorKind::WrongEtag(href.to_owned()))? + Err(WrongEtag { + href: href.to_owned(), + })? } } - Vacant(_) => Err(ErrorKind::ItemNotFound(href.to_owned()))?, + Vacant(_) => Err(ItemNotFound { + href: href.to_owned(), + })?, } self.write_back()?; Ok(()) @@ -138,7 +149,7 @@ impl Storage for SinglefileStorage { self.buffered_mode = true; } - fn flush(&mut self) -> Result<()> { + fn flush(&mut self) -> Fallible<()> { if !self.dirty_cache { return Ok(()); } @@ -146,16 +157,21 @@ impl Storage for SinglefileStorage { let af = AtomicFile::new(&self.path, AllowOverwrite); let content = join_collection(items.into_iter().map(|(_, (item, _))| item))?; - af.write::<(), Error, _>(|f| { - f.write_all(content.as_bytes())?; - let real_mtime = metadata(&self.path)?.modified()?; + let path = &self.path; + let write_inner = |f: &mut File| -> Fallible<()> { + f.write_all(content.as_bytes())?; + let real_mtime = metadata(path)?.modified()?; if mtime != real_mtime { - Err(ErrorKind::MtimeMismatch( - self.path.to_string_lossy().into_owned(), - ))?; + Err(MtimeMismatch { + filepath: path.to_string_lossy().into_owned(), + })?; } Ok(()) + }; + + af.write::<(), ::failure::Compat<::failure::Error>, _>(|f| { + write_inner(f).map_err(|e| e.compat()) })?; self.dirty_cache = false; @@ -164,10 +180,11 @@ impl Storage for SinglefileStorage { } } -fn split_collection(mut input: &str) -> Result> { +fn split_collection(mut input: &str) -> Fallible> { let mut rv = vec![]; while !input.is_empty() { - let (component, remainder) = vobject::read_component(input)?; + let (component, remainder) = + vobject::read_component(input).map_err(::failure::SyncFailure::new)?; input = remainder; match component.name.as_ref() { @@ -175,17 +192,17 @@ fn split_collection(mut input: &str) -> Result> { "VCARD" => rv.push(component), "VADDRESSBOOK" => for vcard in component.subcomponents { if vcard.name != "VCARD" { - Err(ErrorKind::UnexpectedVobject( - vcard.name.clone(), - "VCARD".to_owned(), - ))?; + Err(UnexpectedVobject { + found: vcard.name.clone(), + expected: "VCARD".to_owned(), + })?; } rv.push(vcard); }, - _ => Err(ErrorKind::UnexpectedVobject( - component.name.clone(), - "VCALENDAR | VCARD | VADDRESSBOOK".to_owned(), - ))?, + _ => Err(UnexpectedVobject { + found: component.name.clone(), + expected: "VCALENDAR | VCARD | VADDRESSBOOK".to_owned(), + })?, } } @@ -194,7 +211,7 @@ fn split_collection(mut input: &str) -> Result> { /// Split one VCALENDAR component into multiple VCALENDAR components #[inline] -fn split_vcalendar(mut vcalendar: vobject::Component) -> Result> { +fn split_vcalendar(mut vcalendar: vobject::Component) -> Fallible> { vcalendar.props.remove("METHOD"); let mut timezones = BTreeMap::new(); // tzid => component @@ -210,10 +227,10 @@ fn split_vcalendar(mut vcalendar: vobject::Component) -> Result subcomponents.push(component), - _ => Err(ErrorKind::UnexpectedVobject( - component.name.clone(), - "VTIMEZONE | VTODO | VEVENT | VJOURNAL".to_owned(), - ))?, + _ => Err(UnexpectedVobject { + found: component.name.clone(), + expected: "VTIMEZONE | VTODO | VEVENT | VJOURNAL".to_owned(), + })?, }; } @@ -262,7 +279,7 @@ fn split_vcalendar(mut vcalendar: vobject::Component) -> Result>(item_iter: I) -> Result { +fn join_collection>(item_iter: I) -> Fallible { let mut items = item_iter.peekable(); let item_name = match items.peek() { @@ -273,10 +290,10 @@ fn join_collection>(item_iter: I) -> Result { let wrapper_name = match item_name.as_ref() { "VCARD" => "VADDRESSBOOK", "VCALENDAR" => "VCALENDAR", - _ => Err(ErrorKind::UnexpectedVobject( - item_name.clone(), - "VCARD | VCALENDAR".to_owned(), - ))?, + _ => Err(UnexpectedVobject { + found: item_name.clone(), + expected: "VCARD | VCALENDAR".to_owned(), + })?, }; let mut wrapper = vobject::Component::new(wrapper_name); @@ -285,17 +302,20 @@ fn join_collection>(item_iter: I) -> Result { for item in items { let mut c = item.into_component()?; if c.name != item_name { - return Err(ErrorKind::UnexpectedVobject(c.name, item_name.clone()).into()); + return Err(UnexpectedVobject { + found: c.name, + expected: item_name.clone(), + }.into()); } if item_name == wrapper_name { wrapper.subcomponents.extend(c.subcomponents.drain(..)); match (version.as_ref(), c.get_only("VERSION")) { (Some(x), Some(y)) if x.raw_value != y.raw_value => { - return Err(ErrorKind::VobjectVersionMismatch( - x.raw_value.clone(), - y.raw_value.clone(), - ).into()); + return Err(UnexpectedVobjectVersion { + expected: x.raw_value.clone(), + found: y.raw_value.clone(), + }.into()); } (None, Some(_)) => (), _ => continue, diff --git a/rust/vdirsyncer_rustext.h b/rust/vdirsyncer_rustext.h new file mode 100644 index 0000000..4d89afe --- /dev/null +++ b/rust/vdirsyncer_rustext.h @@ -0,0 +1,90 @@ +#include +#include +#include + +typedef struct Box_Storage Box_Storage; + +typedef struct Item Item; + +typedef struct ShippaiError ShippaiError; + +typedef struct VdirsyncerStorageListing VdirsyncerStorageListing; + +typedef struct { + Item *item; + const char *etag; +} VdirsyncerStorageGetResult; + +typedef struct { + const char *href; + const char *etag; +} VdirsyncerStorageUploadResult; + +void shippai_free_failure(ShippaiError *t); + +void shippai_free_str(char *t); + +const char *shippai_get_cause_display(ShippaiError *t); + +const char *shippai_get_cause_name(ShippaiError *t); + +const char *shippai_get_cause_names(void); + +const char *shippai_get_debug(ShippaiError *t); + +bool vdirsyncer_advance_storage_listing(VdirsyncerStorageListing *listing); + +void vdirsyncer_free_item(Item *c); + +void vdirsyncer_free_storage_get_result(VdirsyncerStorageGetResult *res); + +void vdirsyncer_free_storage_listing(VdirsyncerStorageListing *listing); + +void vdirsyncer_free_storage_upload_result(VdirsyncerStorageUploadResult *res); + +void vdirsyncer_free_str(const char *s); + +const char *vdirsyncer_get_hash(Item *c, ShippaiError **err); + +const char *vdirsyncer_get_raw(Item *c); + +const char *vdirsyncer_get_uid(Item *c); + +Box_Storage *vdirsyncer_init_singlefile(const char *path); + +Item *vdirsyncer_item_from_raw(const char *s); + +bool vdirsyncer_item_is_parseable(Item *c); + +void vdirsyncer_storage_buffered(Box_Storage *storage); + +void vdirsyncer_storage_delete(Box_Storage *storage, + const char *c_href, + const char *c_etag, + ShippaiError **err); + +void vdirsyncer_storage_flush(Box_Storage *storage, ShippaiError **err); + +void vdirsyncer_storage_free(Box_Storage *storage); + +VdirsyncerStorageGetResult *vdirsyncer_storage_get(Box_Storage *storage, + const char *c_href, + ShippaiError **err); + +VdirsyncerStorageListing *vdirsyncer_storage_list(Box_Storage *storage, ShippaiError **err); + +const char *vdirsyncer_storage_listing_get_etag(VdirsyncerStorageListing *listing); + +const char *vdirsyncer_storage_listing_get_href(VdirsyncerStorageListing *listing); + +const char *vdirsyncer_storage_update(Box_Storage *storage, + const char *c_href, + Item *item, + const char *c_etag, + ShippaiError **err); + +VdirsyncerStorageUploadResult *vdirsyncer_storage_upload(Box_Storage *storage, + Item *item, + ShippaiError **err); + +Item *vdirsyncer_with_uid(Item *c, const char *uid, ShippaiError **err); diff --git a/setup.py b/setup.py index 93aa988..bea282b 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,10 @@ requirements = [ # https://github.com/untitaker/python-atomicwrites/commit/4d12f23227b6a944ab1d99c507a69fdbc7c9ed6d # noqa 'atomicwrites>=0.1.7', - milksnake + + milksnake, + + 'shippai >= 0.1.1', ] @@ -48,8 +51,7 @@ def build_native(spec): module_path='vdirsyncer._native', dylib=lambda: build.find_dylib( 'vdirsyncer_rustext', in_path='rust/target/release'), - header_filename=lambda: build.find_header( - 'vdirsyncer_rustext.h', in_path='rust/target'), + header_filename='rust/vdirsyncer_rustext.h', # Rust bug: If thread-local storage is used, this flag is necessary # (mitsuhiko) rtld_flags=['NOW', 'NODELETE'] diff --git a/vdirsyncer/native.py b/vdirsyncer/native.py index 22f88a8..9186788 100644 --- a/vdirsyncer/native.py +++ b/vdirsyncer/native.py @@ -1,5 +1,10 @@ -from ._native import ffi, lib +import shippai + from . import exceptions +from ._native import ffi, lib + + +errors = shippai.Shippai(ffi, lib) def string_rv(c_str): @@ -13,20 +18,16 @@ def item_rv(c): return ffi.gc(c, lib.vdirsyncer_free_item) +def get_error_pointer(): + return ffi.new("ShippaiError **") + + def check_error(e): try: - if e.failed: - msg = ffi.string(e.msg).decode('utf-8') - if msg.startswith('ItemNotFound'): - raise exceptions.NotFoundError(msg) - elif msg.startswith('AlreadyExisting'): - raise exceptions.AlreadyExistingError(msg) - elif msg.startswith('WrongEtag'): - raise exceptions.WrongEtagError(msg) - elif msg.startswith('ItemUnparseable'): - raise ValueError(msg) - else: - raise Exception(msg) - finally: - if e.failed: - lib.vdirsyncer_clear_err(e) + errors.check_exception(e[0]) + except errors.ItemNotFound as e: + raise exceptions.NotFoundError(e) + except errors.ItemAlreadyExisting as e: + raise exceptions.AlreadyExistingError(e) + except errors.WrongEtag as e: + raise exceptions.WrongEtagError(e) diff --git a/vdirsyncer/storage/_rust.py b/vdirsyncer/storage/_rust.py index 87732aa..9f63c98 100644 --- a/vdirsyncer/storage/_rust.py +++ b/vdirsyncer/storage/_rust.py @@ -8,13 +8,12 @@ class RustStorageMixin: def _native(self, name): return partial( - getattr(native.lib, - 'vdirsyncer_{}_{}'.format(self.storage_name, name)), + getattr(native.lib, 'vdirsyncer_storage_{}'.format(name)), self._native_storage ) def list(self): - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() listing = self._native('list')(e) native.check_error(e) listing = native.ffi.gc(listing, @@ -28,7 +27,7 @@ class RustStorageMixin: def get(self, href): href = href.encode('utf-8') - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() result = self._native('get')(href, e) native.check_error(e) result = native.ffi.gc(result, @@ -40,7 +39,7 @@ class RustStorageMixin: # FIXME: implement get_multi def upload(self, item): - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() result = self._native('upload')(item._native, e) native.check_error(e) result = native.ffi.gc( @@ -52,7 +51,7 @@ class RustStorageMixin: def update(self, href, item, etag): href = href.encode('utf-8') etag = etag.encode('utf-8') - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() etag = self._native('update')(href, item._native, etag, e) native.check_error(e) return native.string_rv(etag) @@ -60,7 +59,7 @@ class RustStorageMixin: def delete(self, href, etag): href = href.encode('utf-8') etag = etag.encode('utf-8') - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() self._native('delete')(href, etag, e) native.check_error(e) @@ -68,6 +67,6 @@ class RustStorageMixin: self._native('buffered')() def flush(self): - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() self._native('flush')(e) native.check_error(e) diff --git a/vdirsyncer/storage/singlefile.py b/vdirsyncer/storage/singlefile.py index 40665f6..31708f9 100644 --- a/vdirsyncer/storage/singlefile.py +++ b/vdirsyncer/storage/singlefile.py @@ -78,7 +78,7 @@ class SingleFileStorage(RustStorageMixin, Storage): self._native_storage = native.ffi.gc( native.lib.vdirsyncer_init_singlefile(path.encode('utf-8')), - native.lib.vdirsyncer_free_singlefile + native.lib.vdirsyncer_storage_free ) @classmethod diff --git a/vdirsyncer/vobject.py b/vdirsyncer/vobject.py index 4ff474f..b204e0a 100644 --- a/vdirsyncer/vobject.py +++ b/vdirsyncer/vobject.py @@ -27,7 +27,7 @@ class Item(object): new_uid = new_uid or '' assert isinstance(new_uid, str), type(new_uid) - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() rv = native.lib.vdirsyncer_with_uid(self._native, new_uid.encode('utf-8'), e) @@ -49,7 +49,7 @@ class Item(object): @cached_property def hash(self): - e = native.ffi.new('VdirsyncerError *') + e = native.get_error_pointer() rv = native.lib.vdirsyncer_get_hash(self._native, e) native.check_error(e) return native.string_rv(rv)