Refactor rust errors (#722)

Refactor rust errors
This commit is contained in:
Markus Unterwaditzer 2018-03-03 22:43:28 +01:00 committed by GitHub
parent 3f41f9cf41
commit 06d59f59a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 561 additions and 502 deletions

View file

@ -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

1
rust/.gitignore vendored
View file

@ -1,2 +1 @@
target/
src/storage/exports.rs

45
rust/Cargo.lock generated
View file

@ -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"

View file

@ -1,8 +1,7 @@
[package]
name = "vdirsyncer_rustext"
name = "vdirsyncer-rustext"
version = "0.1.0"
authors = ["Markus Unterwaditzer <markus@unterwaditzer.net>"]
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]

View file

@ -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),
}
}

4
rust/cbindgen.toml Normal file
View file

@ -0,0 +1,4 @@
language = "C"
[parse]
expand = ["vdirsyncer-rustext"]

74
rust/src/errors.rs Normal file
View file

@ -0,0 +1,74 @@
use failure;
pub type Fallible<T> = Result<T, failure::Error>;
#[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<V>(
res: Result<V, failure::Error>,
c_err: *mut *mut exports::ShippaiError,
) -> Option<V> {
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
}
}

View file

@ -41,13 +41,13 @@ impl Item {
None
}
pub fn with_uid(&self, uid: &str) -> Result<Self> {
pub fn with_uid(&self, uid: &str) -> Fallible<Self> {
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<vobject::Component> {
pub fn into_component(self) -> Fallible<vobject::Component> {
match self {
Item::Parsed(component) => Ok(component),
_ => Err(ErrorKind::ItemUnparseable.into()),
_ => Err(ItemUnparseable.into()),
}
}
/// Used for etags
pub fn get_hash(&self) -> Result<String> {
pub fn get_hash(&self) -> Fallible<String> {
// 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<String> {
pub fn get_ident(&self) -> Fallible<String> {
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()
}
}

View file

@ -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<atomicwrites::Error<Error>> for Error {
fn from(e: atomicwrites::Error<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();
}
}

193
rust/src/storage/exports.rs Normal file
View file

@ -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<Storage>) {
let _: Box<Box<Storage>> = Box::from_raw(storage);
}
#[no_mangle]
pub unsafe extern "C" fn vdirsyncer_storage_list(
storage: *mut Box<Storage>,
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<Storage>,
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<Storage>,
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<Storage>,
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<Storage>,
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>) {
(**storage).buffered();
}
#[no_mangle]
pub unsafe extern "C" fn vdirsyncer_storage_flush(
storage: *mut Box<Storage>,
err: *mut *mut exports::ShippaiError,
) {
let _ = export_result((**storage).flush(), err);
}
pub struct VdirsyncerStorageListing {
iterator: Box<Iterator<Item = (String, String)>>,
href: Option<String>,
etag: Option<String>,
}
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<String> {
self.href.take()
}
pub fn get_etag(&mut self) -> Option<String> {
self.etag.take()
}
}
#[no_mangle]
pub unsafe extern "C" fn vdirsyncer_free_storage_listing(listing: *mut VdirsyncerStorageListing) {
let _: Box<VdirsyncerStorageListing> = 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<VdirsyncerStorageGetResult> = 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<VdirsyncerStorageUploadResult> = Box::from_raw(res);
}

View file

@ -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<Box<Iterator<Item = (String, String)> + 'a>>;
fn list<'a>(&'a mut self) -> Fallible<Box<Iterator<Item = (String, String)> + '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<ItemAndEtag>;
/// 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<Item = String> + 'a>(
&'a mut self,
hrefs: I,
) -> Box<Iterator<Item = (String, Result<ItemAndEtag>)> + 'a> {
Box::new(DefaultGetMultiIterator {
storage: self,
href_iter: hrefs,
})
}
fn get(&mut self, href: &str) -> Fallible<ItemAndEtag>;
/// 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<String>;
fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible<String>;
/// 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<Item = String>> {
storage: &'a mut S,
href_iter: I,
}
impl<'a, S, I> Iterator for DefaultGetMultiIterator<'a, S, I>
where
S: Storage,
I: Iterator<Item = String>,
{
type Item = (String, Result<ItemAndEtag>);
fn next(&mut self) -> Option<Self::Item> {
match self.href_iter.next() {
Some(x) => Some((x.to_owned(), self.storage.get(&x))),
None => None,
}
}
}
pub struct VdirsyncerStorageListing {
iterator: Box<Iterator<Item = (String, String)>>,
href: Option<String>,
etag: Option<String>,
}
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<String> {
self.href.take()
}
pub fn get_etag(&mut self) -> Option<String> {
self.etag.take()
}
}
#[no_mangle]
pub unsafe extern "C" fn vdirsyncer_free_storage_listing(listing: *mut VdirsyncerStorageListing) {
let _: Box<VdirsyncerStorageListing> = 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<VdirsyncerStorageGetResult> = 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<VdirsyncerStorageUploadResult> = Box::from_raw(res);
}

View file

@ -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<SinglefileStorage> = Box::from_raw(s);
#[no_mangle]
pub unsafe extern "C" fn vdirsyncer_init_singlefile(path: *const c_char) -> *mut Box<Storage> {
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<Box<Iterator<Item = (String, String)> + 'a>> {
fn list<'a>(&'a mut self) -> Fallible<Box<Iterator<Item = (String, String)> + '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<String> {
fn update(&mut self, href: &str, item: Item, etag: &str) -> Fallible<String> {
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<Vec<vobject::Component>> {
fn split_collection(mut input: &str) -> Fallible<Vec<vobject::Component>> {
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<Vec<vobject::Component>> {
"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<Vec<vobject::Component>> {
/// Split one VCALENDAR component into multiple VCALENDAR components
#[inline]
fn split_vcalendar(mut vcalendar: vobject::Component) -> Result<Vec<vobject::Component>> {
fn split_vcalendar(mut vcalendar: vobject::Component) -> Fallible<Vec<vobject::Component>> {
vcalendar.props.remove("METHOD");
let mut timezones = BTreeMap::new(); // tzid => component
@ -210,10 +227,10 @@ fn split_vcalendar(mut vcalendar: vobject::Component) -> Result<Vec<vobject::Com
timezones.insert(tzid, component);
}
"VTODO" | "VEVENT" | "VJOURNAL" => 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<Vec<vobject::Com
.collect())
}
fn join_collection<I: Iterator<Item = Item>>(item_iter: I) -> Result<String> {
fn join_collection<I: Iterator<Item = Item>>(item_iter: I) -> Fallible<String> {
let mut items = item_iter.peekable();
let item_name = match items.peek() {
@ -273,10 +290,10 @@ fn join_collection<I: Iterator<Item = Item>>(item_iter: I) -> Result<String> {
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<I: Iterator<Item = Item>>(item_iter: I) -> Result<String> {
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,

90
rust/vdirsyncer_rustext.h Normal file
View file

@ -0,0 +1,90 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
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);

View file

@ -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']

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)