Merge pull request #1110 from matthiasbeyer/libimagcontact/init

Libimagcontact/init
This commit is contained in:
Matthias Beyer 2017-11-08 18:15:02 +01:00 committed by GitHub
commit 6efd0a9450
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 470 additions and 0 deletions

View file

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

View file

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

View file

@ -0,0 +1,26 @@
[package]
name = "libimagcontact"
version = "0.5.0"
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
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/" }

View file

@ -0,0 +1 @@
../../../doc/src/05100-lib-contacts.md

View file

@ -0,0 +1,90 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<bool>;
// getting data
fn get_contact_data(&self) -> Result<ContactData>;
// More convenience functionality may follow
}
impl Contact for Entry {
fn is_contact(&self) -> Result<bool> {
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<ContactData> {
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
}
}

View file

@ -0,0 +1,52 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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)
}
}
}

View file

@ -0,0 +1,69 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<FileLockEntry<'a>>;
fn next(&mut self) -> Option<Self::Item> {
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)),
},
},
}
}
}
}

View file

@ -0,0 +1,53 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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;

View file

@ -0,0 +1,86 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<FileLockEntry<'a>>;
/// 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<FileLockEntry<'a>>;
// getting
fn all_contacts(&'a self) -> Result<StoreIdIterator>;
}
/// 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<FileLockEntry<'a>> {
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<FileLockEntry<'a>> {
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<StoreIdIterator> {
self.all_references().map_err(From::from)
}
}

View file

@ -0,0 +1,45 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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<A: AsRef<Path> + Debug>(pb: A) -> Result<String> {
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<Component> {
use vobject::parse_component;
parse_component(&buf).map_err(From::from)
}