diff --git a/Cargo.toml b/Cargo.toml index e9276006..21f3a85e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "lib/core/libimagrt", "lib/core/libimagstore", "lib/domain/libimagbookmark", + "lib/domain/libimagcontact", "lib/domain/libimagdiary", "lib/domain/libimagmail", "lib/domain/libimagnotes", diff --git a/doc/src/05100-lib-contacts.md b/doc/src/05100-lib-contacts.md index 4bdd5371..25e69069 100644 --- a/doc/src/05100-lib-contacts.md +++ b/doc/src/05100-lib-contacts.md @@ -1,2 +1,49 @@ ## libimagcontacts +The contact library basically only creates references to the actual icalendar +and vcard files, though it also can parse (via the `vobject` crate) the +information and return it from an entry directly. + +The architecture of indirections is as follows: + +```{.numberLines} + ++--------------------------------+ +| | +| Store, as ContactStore | +| | ++----------------+---------------+ + | + | Provides access to + | ++----------------v---------------+ +| | +| (FileLock)Entry as Contact | +| | +| which is actually a: | +| | +| (FileLock)Entry as Ref | +| | ++----------------+---------------+ + | + | refers to + | ++----------------v---------------+ +| | +| vcard file (outside store) | +| | ++----------------+---------------+ + | + | contains + | ++----------------v---------------+ +| | +| vcard data | +| | ++--------------------------------+ + +``` + +As the library is build upon `libimagentryref`, it does not create a new +subcollection in the store `/contacts`, but uses the infrastructure of +`libimagentryref` which automatically puts all references in `/ref`. diff --git a/lib/domain/libimagcontact/Cargo.toml b/lib/domain/libimagcontact/Cargo.toml new file mode 100644 index 00000000..acc420aa --- /dev/null +++ b/lib/domain/libimagcontact/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "libimagcontact" +version = "0.5.0" +authors = ["Matthias Beyer "] + +description = "Library for the imag core distribution" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../../../README.md" +license = "LGPL-2.1" + +documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[dependencies] +error-chain = "0.11" +log = "0.3" +toml = "0.4" +toml-query = "0.4" +vobject = { git = 'https://github.com/matthiasbeyer/rust-vobject', branch = "next" } + +libimagstore = { version = "0.5.0", path = "../../../lib/core/libimagstore" } +libimagerror = { version = "0.5.0", path = "../../../lib/core/libimagerror" } +libimagentryref = { version = "0.5.0", path = "../../../lib/entry/libimagentryref/" } + diff --git a/lib/domain/libimagcontact/README.md b/lib/domain/libimagcontact/README.md new file mode 120000 index 00000000..e097eece --- /dev/null +++ b/lib/domain/libimagcontact/README.md @@ -0,0 +1 @@ +../../../doc/src/05100-lib-contacts.md \ No newline at end of file diff --git a/lib/domain/libimagcontact/src/contact.rs b/lib/domain/libimagcontact/src/contact.rs new file mode 100644 index 00000000..14a80e3b --- /dev/null +++ b/lib/domain/libimagcontact/src/contact.rs @@ -0,0 +1,90 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::ops::Deref; + +use vobject::Component; +use toml::Value; +use toml_query::read::TomlValueReadExt; + +use libimagstore::store::Entry; +use libimagentryref::reference::Ref; + +use error::ContactError as CE; +use error::ContactErrorKind as CEK; +use error::Result; +use util; + +/// Trait to be implemented on ::libimagstore::store::Entry +/// +/// Based on the functionality from libimagentryref, for fetching the Ical data from disk +pub trait Contact : Ref { + + fn is_contact(&self) -> Result; + + // getting data + + fn get_contact_data(&self) -> Result; + + // More convenience functionality may follow + +} + +impl Contact for Entry { + + fn is_contact(&self) -> Result { + let location = "contact.marker"; + match self.get_header().read(location)? { + Some(&Value::Boolean(b)) => Ok(b), + Some(_) => Err(CE::from_kind(CEK::HeaderTypeError("boolean", location))), + None => Ok(false) + } + } + + fn get_contact_data(&self) -> Result { + let component = self + .fs_file() + .map_err(From::from) + .and_then(util::read_to_string) + .and_then(util::parse)?; + + Ok(ContactData(component)) + } + +} + +pub struct ContactData(Component); + +impl ContactData { + + pub fn into_inner(self) -> Component { + self.0 + } + +} + +impl Deref for ContactData { + type Target = Component; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + + diff --git a/lib/domain/libimagcontact/src/error.rs b/lib/domain/libimagcontact/src/error.rs new file mode 100644 index 00000000..c3c81472 --- /dev/null +++ b/lib/domain/libimagcontact/src/error.rs @@ -0,0 +1,52 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagstore::storeid::StoreId; + +error_chain! { + types { + ContactError, ContactErrorKind, ResultExt, Result; + } + + links { + StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind); + RefError(::libimagentryref::error::RefError, ::libimagentryref::error::RefErrorKind); + VObjectError(::vobject::error::VObjectError, ::vobject::error::VObjectErrorKind); + } + + foreign_links { + Io(::std::io::Error); + TomlQueryError(::toml_query::error::Error); + } + + errors { + + HeaderTypeError(ty: &'static str, loc: &'static str) { + description("Type error in header") + display("Type error in header, expected {} at '{}', found other type", ty, loc) + } + + EntryNotFound(sid: StoreId) { + description("Entry not found with StoreId") + display("Entry {:?} not found", sid) + } + + } +} + diff --git a/lib/domain/libimagcontact/src/iter.rs b/lib/domain/libimagcontact/src/iter.rs new file mode 100644 index 00000000..b5f8974a --- /dev/null +++ b/lib/domain/libimagcontact/src/iter.rs @@ -0,0 +1,69 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagstore::storeid::StoreIdIterator; +use libimagstore::store::Store; +use libimagstore::store::FileLockEntry; + +use contact::Contact; +use error::ContactError as CE; +use error::ContactErrorKind as CEK; +use error::Result; + +pub struct ContactIter<'a>(StoreIdIterator, &'a Store); + +/// Iterator over contacts +/// +/// As the libimagcontact works with libimagentryref in the backend, we must hold a reference to the +/// Store here as well, so we can check whether a fetched StoreId actually points to a contact +/// reference or not. +/// +/// So, the Iterator `Store::get()`s the object pointed to by the StoreId and returns it if +/// everything worked. +impl<'a> ContactIter<'a> { + + pub fn new(sii: StoreIdIterator, store: &'a Store) -> ContactIter<'a> { + ContactIter(sii, store) + } + +} + +impl<'a> Iterator for ContactIter<'a> { + type Item = Result>; + + fn next(&mut self) -> Option { + loop { + match self.0.next() { + None => return None, + Some(sid) => match self.1.get(sid.clone()).map_err(From::from) { + Err(e) => return Some(Err(e)), + Ok(None) => return Some(Err(CE::from_kind(CEK::EntryNotFound(sid)))), + Ok(Some(entry)) => match entry.is_contact().map_err(From::from) { + Ok(true) => return Some(Ok(entry)), + Ok(false) => continue, + Err(e) => return Some(Err(e)), + }, + + }, + } + } + } + +} + diff --git a/lib/domain/libimagcontact/src/lib.rs b/lib/domain/libimagcontact/src/lib.rs new file mode 100644 index 00000000..efdb8a60 --- /dev/null +++ b/lib/domain/libimagcontact/src/lib.rs @@ -0,0 +1,53 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +#![deny( + dead_code, + non_camel_case_types, + non_snake_case, + path_statements, + trivial_numeric_casts, + unstable_features, + unused_allocation, + unused_import_braces, + unused_imports, + unused_must_use, + unused_mut, + unused_qualifications, + while_true, +)] + +#[macro_use] extern crate log; +#[macro_use] extern crate error_chain; +extern crate vobject; +extern crate toml; +extern crate toml_query; + +#[macro_use] extern crate libimagstore; +extern crate libimagerror; +extern crate libimagentryref; + +module_entry_path_mod!("contact"); + +pub mod contact; +pub mod error; +pub mod iter; +pub mod store; +mod util; + diff --git a/lib/domain/libimagcontact/src/store.rs b/lib/domain/libimagcontact/src/store.rs new file mode 100644 index 00000000..770c08ff --- /dev/null +++ b/lib/domain/libimagcontact/src/store.rs @@ -0,0 +1,86 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::path::PathBuf; + +use vobject::parse_component; +use toml::Value; +use toml_query::insert::TomlValueInsertExt; + +use libimagstore::store::Store; +use libimagstore::store::FileLockEntry; +use libimagstore::storeid::StoreIdIterator; +use libimagentryref::refstore::RefStore; +use libimagentryref::flags::RefFlags; + +use error::Result; +use util; + +pub trait ContactStore<'a> : RefStore { + + // creating + + fn create_from_path(&'a self, p: &PathBuf) -> Result>; + + /// Create contact ref from buffer + /// + /// Needs the `p` argument as we're finally creating a reference by path, the buffer is only for + /// collecting metadata. + fn create_from_buf(&'a self, p: &PathBuf, buf: &String) -> Result>; + + // getting + + fn all_contacts(&'a self) -> Result; +} + +/// The extension for the Store to work with contacts +/// +/// The contact functionality is implemented by using the `libimagentryref` library, so basically +/// we only reference vcard files from outside the store. +/// +/// Because of this, we do not have an own store collection `/contacts` or something like that, but +/// must stress the `libimagentryref` API for everything. +impl<'a> ContactStore<'a> for Store { + + fn create_from_path(&'a self, p: &PathBuf) -> Result> { + util::read_to_string(p).and_then(|buf| self.create_from_buf(p, &buf)) + } + + /// Create contact ref from buffer + fn create_from_buf(&'a self, p: &PathBuf, buf: &String) -> Result> { + let component = parse_component(&buf)?; + debug!("Parsed: {:?}", component); + + let flags = RefFlags::default().with_content_hashing(true).with_permission_tracking(false); + RefStore::create(self, p.clone(), flags) + .map_err(From::from) + .and_then(|mut entry| { + entry.get_header_mut() + .insert("contact.marker", Value::Boolean(true)) + .map_err(From::from) + .map(|_| entry) + }) + } + + fn all_contacts(&'a self) -> Result { + self.all_references().map_err(From::from) + } + +} + diff --git a/lib/domain/libimagcontact/src/util.rs b/lib/domain/libimagcontact/src/util.rs new file mode 100644 index 00000000..0dad5978 --- /dev/null +++ b/lib/domain/libimagcontact/src/util.rs @@ -0,0 +1,45 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::path::Path; +use std::fmt::Debug; +use std::fs::File; +use std::io::Read; + +use error::Result; + +use vobject::Component; + +pub fn read_to_string + Debug>(pb: A) -> Result { + let mut cont = String::new(); + + let mut file = File::open(pb.as_ref())?; + let bytes = file.read_to_string(&mut cont)?; + + debug!("Read {} bytes from {:?}", bytes, pb); + + Ok(cont) +} + +/// Helper for chaining results nicely +pub fn parse(buf: String) -> Result { + use vobject::parse_component; + parse_component(&buf).map_err(From::from) +} +