Merge pull request #1450 from matthiasbeyer/libimagcontact/not-based-on-libimagentryref
libimagcontact: not based on libimagentryref
This commit is contained in:
commit
2b10ab0b36
12 changed files with 352 additions and 314 deletions
|
@ -38,12 +38,7 @@ libimagutil = { version = "0.8.0", path = "../../../lib/etc/libimagutil"
|
||||||
libimagentryref = { version = "0.8.0", path = "../../../lib/entry/libimagentryref" }
|
libimagentryref = { version = "0.8.0", path = "../../../lib/entry/libimagentryref" }
|
||||||
libimagentryedit = { version = "0.8.0", path = "../../../lib/entry/libimagentryedit" }
|
libimagentryedit = { version = "0.8.0", path = "../../../lib/entry/libimagentryedit" }
|
||||||
libimaginteraction = { version = "0.8.0", path = "../../../lib/etc/libimaginteraction" }
|
libimaginteraction = { version = "0.8.0", path = "../../../lib/etc/libimaginteraction" }
|
||||||
|
libimagcontact = { version = "0.8.0", path = "../../../lib/domain/libimagcontact" }
|
||||||
[dependencies.libimagcontact]
|
|
||||||
version = "0.8.0"
|
|
||||||
path = "../../../lib/domain/libimagcontact"
|
|
||||||
default-features = false
|
|
||||||
features = ["deser"]
|
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "^2.29"
|
version = "^2.29"
|
||||||
|
|
|
@ -44,14 +44,13 @@ use toml_query::read::TomlValueReadExt;
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use libimagcontact::store::ContactStore;
|
||||||
use libimagcontact::error::ContactError as CE;
|
use libimagcontact::error::ContactError as CE;
|
||||||
use libimagcontact::store::UniqueContactPathGenerator;
|
|
||||||
use libimagrt::runtime::Runtime;
|
use libimagrt::runtime::Runtime;
|
||||||
use libimagerror::str::ErrFromStr;
|
use libimagerror::str::ErrFromStr;
|
||||||
use libimagerror::trace::MapErrTrace;
|
use libimagerror::trace::MapErrTrace;
|
||||||
use libimagerror::trace::trace_error;
|
use libimagerror::trace::trace_error;
|
||||||
use libimagutil::warn_result::WarnResult;
|
use libimagutil::warn_result::WarnResult;
|
||||||
use libimagentryref::refstore::RefStore;
|
|
||||||
|
|
||||||
const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");
|
const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");
|
||||||
|
|
||||||
|
@ -158,7 +157,8 @@ pub fn create(rt: &Runtime) {
|
||||||
|
|
||||||
if let Some(location) = location {
|
if let Some(location) = location {
|
||||||
if !scmd.is_present("dont-track") {
|
if !scmd.is_present("dont-track") {
|
||||||
RefStore::create_ref::<UniqueContactPathGenerator, _>(rt.store(), location)
|
rt.store()
|
||||||
|
.create_from_path(&location)
|
||||||
.map_err_trace_exit_unwrap(1);
|
.map_err_trace_exit_unwrap(1);
|
||||||
|
|
||||||
info!("Created entry in store");
|
info!("Created entry in store");
|
||||||
|
|
|
@ -48,7 +48,6 @@ extern crate libimagstore;
|
||||||
extern crate libimagerror;
|
extern crate libimagerror;
|
||||||
extern crate libimagutil;
|
extern crate libimagutil;
|
||||||
extern crate libimaginteraction;
|
extern crate libimaginteraction;
|
||||||
extern crate libimagentryref;
|
|
||||||
extern crate libimagentryedit;
|
extern crate libimagentryedit;
|
||||||
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
@ -57,7 +56,6 @@ use std::io::Write;
|
||||||
|
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use vobject::vcard::Vcard;
|
|
||||||
use toml_query::read::TomlValueReadTypeExt;
|
use toml_query::read::TomlValueReadTypeExt;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
@ -67,14 +65,12 @@ use libimagerror::str::ErrFromStr;
|
||||||
use libimagerror::trace::MapErrTrace;
|
use libimagerror::trace::MapErrTrace;
|
||||||
use libimagerror::io::ToExitCode;
|
use libimagerror::io::ToExitCode;
|
||||||
use libimagerror::exit::ExitUnwrap;
|
use libimagerror::exit::ExitUnwrap;
|
||||||
|
use libimagerror::iter::TraceIterator;
|
||||||
use libimagcontact::store::ContactStore;
|
use libimagcontact::store::ContactStore;
|
||||||
use libimagcontact::store::UniqueContactPathGenerator;
|
|
||||||
use libimagcontact::error::ContactError as CE;
|
use libimagcontact::error::ContactError as CE;
|
||||||
use libimagcontact::contact::Contact;
|
use libimagcontact::contact::Contact;
|
||||||
use libimagcontact::deser::DeserVcard;
|
use libimagcontact::deser::DeserVcard;
|
||||||
use libimagstore::iter::get::StoreIdGetIteratorExtension;
|
use libimagstore::iter::get::StoreIdGetIteratorExtension;
|
||||||
use libimagentryref::reference::Ref;
|
|
||||||
use libimagentryref::refstore::RefStore;
|
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -128,24 +124,13 @@ fn list(rt: &Runtime) {
|
||||||
.ok_or_else(|| CE::from("StoreId not found".to_owned()))
|
.ok_or_else(|| CE::from("StoreId not found".to_owned()))
|
||||||
.map_err_trace_exit_unwrap(1);
|
.map_err_trace_exit_unwrap(1);
|
||||||
|
|
||||||
fle
|
fle.deser().map_err_trace_exit_unwrap(1)
|
||||||
.get_contact_data()
|
|
||||||
.map(|cd| (fle, cd))
|
|
||||||
.map(|(fle, cd)| (fle, cd.into_inner()))
|
|
||||||
.map(|(fle, cd)| {
|
|
||||||
let card = Vcard::from_component(cd).unwrap_or_else(|e| {
|
|
||||||
error!("Element is not a VCARD object: {:?}", e);
|
|
||||||
exit(1)
|
|
||||||
});
|
|
||||||
(fle, card)
|
|
||||||
})
|
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
})
|
})
|
||||||
.enumerate();
|
.enumerate();
|
||||||
|
|
||||||
if scmd.is_present("json") {
|
if scmd.is_present("json") {
|
||||||
let v : Vec<DeserVcard> = iterator
|
let v : Vec<DeserVcard> = iterator.map(|tpl| tpl.1).collect();
|
||||||
.map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect();
|
|
||||||
match ::serde_json::to_string(&v) {
|
match ::serde_json::to_string(&v) {
|
||||||
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -155,9 +140,8 @@ fn list(rt: &Runtime) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iterator
|
iterator
|
||||||
.map(|(i, (fle, vcard))| {
|
.map(|(i, deservcard)| {
|
||||||
let hash = String::from(fle.get_hash().map_err_trace_exit_unwrap(1));
|
let data = build_data_object_for_handlebars(i, &deservcard);
|
||||||
let data = build_data_object_for_handlebars(i, hash, &vcard);
|
|
||||||
|
|
||||||
list_format.render("format", &data)
|
list_format.render("format", &data)
|
||||||
.err_from_str()
|
.err_from_str()
|
||||||
|
@ -217,32 +201,47 @@ fn import(rt: &Runtime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(rt: &Runtime) {
|
fn show(rt: &Runtime) {
|
||||||
let scmd = rt.cli().subcommand_matches("show").unwrap();
|
let scmd = rt.cli().subcommand_matches("show").unwrap();
|
||||||
let hash = scmd.value_of("hash").map(String::from).unwrap(); // safed by clap
|
let hash = scmd.value_of("hash").map(String::from).unwrap(); // safed by clap
|
||||||
|
|
||||||
let contact_data = rt.store()
|
|
||||||
.get_ref::<UniqueContactPathGenerator, _>(hash.clone())
|
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
.ok_or(CE::from(format!("No entry for hash {}", hash)))
|
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
.get_contact_data()
|
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
.into_inner();
|
|
||||||
let vcard = Vcard::from_component(contact_data)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
error!("Element is not a VCARD object: {:?}", e);
|
|
||||||
exit(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
let show_format = get_contact_print_format("contact.show_format", rt, &scmd);
|
let show_format = get_contact_print_format("contact.show_format", rt, &scmd);
|
||||||
let data = build_data_object_for_handlebars(0, hash, &vcard);
|
let out = rt.stdout();
|
||||||
|
let mut outlock = out.lock();
|
||||||
|
|
||||||
let s = show_format
|
rt.store()
|
||||||
.render("format", &data)
|
.all_contacts()
|
||||||
.err_from_str()
|
.map_err_trace_exit_unwrap(1)
|
||||||
.map_err(CE::from)
|
.into_get_iter(rt.store())
|
||||||
.map_err_trace_exit_unwrap(1);
|
.trace_unwrap_exit(1)
|
||||||
let _ = writeln!(::std::io::stdout(), "{}", s).to_exit_code().unwrap_or_exit();
|
.map(|o| o.unwrap_or_else(|| {
|
||||||
|
error!("Failed to get entry");
|
||||||
|
exit(1)
|
||||||
|
}))
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let deser = entry.deser().map_err_trace_exit_unwrap(1);
|
||||||
|
|
||||||
|
if deser.uid()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error!("Could not get StoreId from Store::all_contacts(). This is a BUG!");
|
||||||
|
::std::process::exit(1)
|
||||||
|
})
|
||||||
|
.unwrap() // exited above
|
||||||
|
.starts_with(&hash)
|
||||||
|
{
|
||||||
|
Some(deser)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|elem| {
|
||||||
|
let data = build_data_object_for_handlebars(0, &elem);
|
||||||
|
|
||||||
|
let s = show_format
|
||||||
|
.render("format", &data)
|
||||||
|
.err_from_str()
|
||||||
|
.map_err(CE::from)
|
||||||
|
.map_err_trace_exit_unwrap(1);
|
||||||
|
let _ = writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find(rt: &Runtime) {
|
fn find(rt: &Runtime) {
|
||||||
|
@ -270,30 +269,20 @@ fn find(rt: &Runtime) {
|
||||||
})
|
})
|
||||||
.unwrap() // safed above
|
.unwrap() // safed above
|
||||||
})
|
})
|
||||||
.filter_map(|cont| {
|
.filter_map(|entry| {
|
||||||
let comp = cont
|
let card = entry.deser().map_err_trace_exit_unwrap(1);
|
||||||
.get_contact_data()
|
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let card = Vcard::from_component(comp)
|
|
||||||
.map_err(|_| {
|
|
||||||
error!("Could not build Vcard from {:?}", cont.get_location());
|
|
||||||
::std::process::exit(1)
|
|
||||||
})
|
|
||||||
.unwrap(); // safed above
|
|
||||||
|
|
||||||
let str_contains_any = |s: &String, v: &Vec<String>| {
|
let str_contains_any = |s: &String, v: &Vec<String>| {
|
||||||
v.iter().any(|i| s.contains(i))
|
v.iter().any(|i| s.contains(i))
|
||||||
};
|
};
|
||||||
|
|
||||||
let take = card.adr().iter().any(|a| str_contains_any(a.raw(), &grepstring))
|
let take = card.adr().iter().any(|a| str_contains_any(a, &grepstring))
|
||||||
|| card.email().iter().any(|a| str_contains_any(a.raw(), &grepstring))
|
|| card.email().iter().any(|a| str_contains_any(a, &grepstring))
|
||||||
|| card.fullname().iter().any(|a| str_contains_any(a.raw(), &grepstring));
|
|| card.fullname().iter().any(|a| str_contains_any(a, &grepstring));
|
||||||
|
|
||||||
if take {
|
if take {
|
||||||
// optimization so we don't have to parse again in the next step
|
// optimization so we don't have to parse again in the next step
|
||||||
Some((cont, card))
|
Some((entry, card))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -301,8 +290,8 @@ fn find(rt: &Runtime) {
|
||||||
.enumerate();
|
.enumerate();
|
||||||
|
|
||||||
if scmd.is_present("json") {
|
if scmd.is_present("json") {
|
||||||
let v : Vec<DeserVcard> = iterator
|
let v : Vec<DeserVcard> = iterator.map(|(_, tlp)| tlp.1).collect();
|
||||||
.map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect();
|
|
||||||
match ::serde_json::to_string(&v) {
|
match ::serde_json::to_string(&v) {
|
||||||
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -312,22 +301,22 @@ fn find(rt: &Runtime) {
|
||||||
}
|
}
|
||||||
} else if scmd.is_present("find-id") {
|
} else if scmd.is_present("find-id") {
|
||||||
iterator
|
iterator
|
||||||
.for_each(|(_i, (fle, _card))| {
|
.for_each(|(_i, (entry, _))| {
|
||||||
writeln!(rt.stdout(), "{}", fle.get_location())
|
writeln!(rt.stdout(), "{}", entry.get_location())
|
||||||
.to_exit_code()
|
.to_exit_code()
|
||||||
.unwrap_or_exit();
|
.unwrap_or_exit();
|
||||||
})
|
})
|
||||||
} else if scmd.is_present("find-full-id") {
|
} else if scmd.is_present("find-full-id") {
|
||||||
let storepath = rt.store().path().display();
|
let storepath = rt.store().path().display();
|
||||||
iterator
|
iterator
|
||||||
.for_each(|(_i, (fle, _card))| {
|
.for_each(|(_i, (entry, _))| {
|
||||||
writeln!(rt.stdout(), "{}/{}", storepath, fle.get_location())
|
writeln!(rt.stdout(), "{}/{}", storepath, entry.get_location())
|
||||||
.to_exit_code()
|
.to_exit_code()
|
||||||
.unwrap_or_exit();
|
.unwrap_or_exit();
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
iterator
|
iterator
|
||||||
.for_each(|(i, (fle, card))| {
|
.for_each(|(i, (_, card))| {
|
||||||
let fmt = if scmd.is_present("find-show") {
|
let fmt = if scmd.is_present("find-show") {
|
||||||
&show_format
|
&show_format
|
||||||
} else if scmd.is_present("find-list") {
|
} else if scmd.is_present("find-list") {
|
||||||
|
@ -336,8 +325,7 @@ fn find(rt: &Runtime) {
|
||||||
&list_format
|
&list_format
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash = fle.get_hash().map(String::from).map_err_trace_exit_unwrap(1);
|
let data = build_data_object_for_handlebars(i, &card);
|
||||||
let data = build_data_object_for_handlebars(i, hash, &card);
|
|
||||||
let s = fmt
|
let s = fmt
|
||||||
.render("format", &data)
|
.render("format", &data)
|
||||||
.err_from_str()
|
.err_from_str()
|
||||||
|
|
|
@ -18,105 +18,58 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use vobject::vcard::Vcard;
|
|
||||||
|
|
||||||
pub fn build_data_object_for_handlebars<'a>(i: usize, hash: String, vcard: &Vcard) -> BTreeMap<&'static str, String> {
|
use libimagcontact::deser::DeserVcard;
|
||||||
|
|
||||||
|
pub fn build_data_object_for_handlebars<'a>(i: usize, vcard: &DeserVcard) -> BTreeMap<&'static str, String> {
|
||||||
let mut data = BTreeMap::new();
|
let mut data = BTreeMap::new();
|
||||||
|
|
||||||
|
let process_list = |list: &Vec<String>| {
|
||||||
|
list.iter()
|
||||||
|
.map(String::clone)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
};
|
||||||
|
|
||||||
|
let process_opt = |opt: Option<&String>| {
|
||||||
|
opt.map(String::clone).unwrap_or_else(String::new)
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
data.insert("i" , format!("{}", i));
|
data.insert("i" , format!("{}", i));
|
||||||
|
|
||||||
// The hash (as in libimagentryref) of the contact
|
// The hash (as in libimagentryref) of the contact
|
||||||
data.insert("id" , hash);
|
data.insert("id" , process_opt(vcard.uid()));
|
||||||
|
data.insert("ADR" , process_list(vcard.adr()));
|
||||||
data.insert("ADR" , vcard.adr()
|
data.insert("ANNIVERSARY" , process_opt(vcard.anniversary()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("BDAY" , process_opt(vcard.bday()));
|
||||||
|
data.insert("CATEGORIES" , process_list(vcard.categories()));
|
||||||
data.insert("ANNIVERSARY" , vcard.anniversary()
|
data.insert("CLIENTPIDMAP" , process_opt(vcard.clientpidmap()));
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
data.insert("EMAIL" , process_list(vcard.email()));
|
||||||
|
data.insert("FN" , process_list(vcard.fullname()));
|
||||||
data.insert("BDAY" , vcard.bday()
|
data.insert("GENDER" , process_opt(vcard.gender()));
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
data.insert("GEO" , process_list(vcard.geo()));
|
||||||
|
data.insert("IMPP" , process_list(vcard.impp()));
|
||||||
data.insert("CATEGORIES" , vcard.categories()
|
data.insert("KEY" , process_list(vcard.key()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("LANG" , process_list(vcard.lang()));
|
||||||
|
data.insert("LOGO" , process_list(vcard.logo()));
|
||||||
data.insert("CLIENTPIDMAP" , vcard.clientpidmap()
|
data.insert("MEMBER" , process_list(vcard.member()));
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
data.insert("N" , process_opt(vcard.name()));
|
||||||
|
data.insert("NICKNAME" , process_list(vcard.nickname()));
|
||||||
data.insert("EMAIL" , vcard.email()
|
data.insert("NOTE" , process_list(vcard.note()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("ORG" , process_list(vcard.org()));
|
||||||
|
data.insert("PHOTO" , process_list(vcard.photo()));
|
||||||
data.insert("FN" , vcard.fullname()
|
data.insert("PRIOD" , process_opt(vcard.proid()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("RELATED" , process_list(vcard.related()));
|
||||||
|
data.insert("REV" , process_opt(vcard.rev()));
|
||||||
data.insert("GENDER" , vcard.gender()
|
data.insert("ROLE" , process_list(vcard.role()));
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
data.insert("SOUND" , process_list(vcard.sound()));
|
||||||
|
data.insert("TEL" , process_list(vcard.tel()));
|
||||||
data.insert("GEO" , vcard.geo()
|
data.insert("TITLE" , process_list(vcard.title()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("TZ" , process_list(vcard.tz()));
|
||||||
|
data.insert("UID" , process_opt(vcard.uid()));
|
||||||
data.insert("IMPP" , vcard.impp()
|
data.insert("URL" , process_list(vcard.url()));
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
data.insert("VERSION" , process_opt(vcard.version()));
|
||||||
|
|
||||||
data.insert("KEY" , vcard.key()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("LANG" , vcard.lang()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("LOGO" , vcard.logo()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("MEMBER" , vcard.member()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("N" , vcard.name()
|
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
|
||||||
|
|
||||||
data.insert("NICKNAME" , vcard.nickname()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("NOTE" , vcard.note()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("ORG" , vcard.org()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("PHOTO" , vcard.photo()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("PRIOD" , vcard.proid()
|
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
|
||||||
|
|
||||||
data.insert("RELATED" , vcard.related()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("REV" , vcard.rev()
|
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
|
||||||
|
|
||||||
data.insert("ROLE" , vcard.role()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("SOUND" , vcard.sound()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("TEL" , vcard.tel()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("TITLE" , vcard.title()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("TZ" , vcard.tz()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("UID" , vcard.uid()
|
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
|
||||||
|
|
||||||
data.insert("URL" , vcard.url()
|
|
||||||
.into_iter().map(|c| c.raw().clone()).collect::<Vec<_>>().join(", "));
|
|
||||||
|
|
||||||
data.insert("VERSION" , vcard.version()
|
|
||||||
.map(|c| c.raw().clone()).unwrap_or(String::new()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
data
|
||||||
|
|
|
@ -25,21 +25,11 @@ log = "0.3"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
toml-query = "0.6"
|
toml-query = "0.6"
|
||||||
vobject = "0.4"
|
vobject = "0.4"
|
||||||
uuid = { version = "0.6", features = ["v4"] }
|
uuid = "0.6"
|
||||||
serde = { version = "1", optional = true }
|
serde = "1"
|
||||||
serde_derive = { version = "1", optional = true }
|
serde_derive = "1"
|
||||||
|
|
||||||
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
|
libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" }
|
||||||
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" }
|
||||||
libimagentryutil = { version = "0.8.0", path = "../../../lib/entry/libimagentryutil/" }
|
libimagentryutil = { version = "0.8.0", path = "../../../lib/entry/libimagentryutil/" }
|
||||||
|
|
||||||
[dependencies.libimagentryref]
|
|
||||||
version = "0.8.0"
|
|
||||||
path = "../../../lib/entry/libimagentryref/"
|
|
||||||
default-features = false
|
|
||||||
features = ["generators", "generators-sha1"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
deser = ["serde", "serde_derive"]
|
|
||||||
|
|
||||||
|
|
|
@ -17,28 +17,27 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
//
|
//
|
||||||
|
|
||||||
use std::ops::Deref;
|
use toml::to_string as toml_to_string;
|
||||||
|
use toml::from_str as toml_from_str;
|
||||||
use vobject::Component;
|
use toml_query::read::TomlValueReadExt;
|
||||||
|
|
||||||
use libimagstore::store::Entry;
|
use libimagstore::store::Entry;
|
||||||
use libimagentryref::reference::Ref;
|
|
||||||
use libimagentryutil::isa::Is;
|
use libimagentryutil::isa::Is;
|
||||||
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
||||||
|
|
||||||
|
use deser::DeserVcard;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use util;
|
use error::ContactError as CE;
|
||||||
|
use error::ContactErrorKind as CEK;
|
||||||
|
|
||||||
/// Trait to be implemented on ::libimagstore::store::Entry
|
/// Trait to be implemented on ::libimagstore::store::Entry
|
||||||
///
|
pub trait Contact {
|
||||||
/// Based on the functionality from libimagentryref, for fetching the Ical data from disk
|
|
||||||
pub trait Contact : Ref {
|
|
||||||
|
|
||||||
fn is_contact(&self) -> Result<bool>;
|
fn is_contact(&self) -> Result<bool>;
|
||||||
|
|
||||||
// getting data
|
// getting data
|
||||||
|
|
||||||
fn get_contact_data(&self) -> Result<ContactData>;
|
fn deser(&self) -> Result<DeserVcard>;
|
||||||
|
|
||||||
// More convenience functionality may follow
|
// More convenience functionality may follow
|
||||||
|
|
||||||
|
@ -52,34 +51,18 @@ impl Contact for Entry {
|
||||||
self.is::<IsContact>().map_err(From::from)
|
self.is::<IsContact>().map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_contact_data(&self) -> Result<ContactData> {
|
fn deser(&self) -> Result<DeserVcard> {
|
||||||
let component = self
|
let data = self
|
||||||
.get_path()
|
.get_header()
|
||||||
.map_err(From::from)
|
.read("contact.data")?
|
||||||
.and_then(util::read_to_string)
|
.ok_or_else(|| CE::from_kind(CEK::HeaderDataMissing("contact.data")))?;
|
||||||
.and_then(util::parse)?;
|
|
||||||
|
|
||||||
Ok(ContactData(component))
|
// ugly hack
|
||||||
|
let data_str = toml_to_string(&data)?;
|
||||||
|
let deser : DeserVcard = toml_from_str(&data_str)?;
|
||||||
|
|
||||||
|
Ok(deser)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,103 +20,127 @@
|
||||||
use vobject::vcard::Vcard;
|
use vobject::vcard::Vcard;
|
||||||
|
|
||||||
/// A type which can be build from a Vcard and be serialized.
|
/// A type which can be build from a Vcard and be serialized.
|
||||||
///
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
/// # Details
|
|
||||||
///
|
|
||||||
/// Deserializing is not supported by libimagcontact yet
|
|
||||||
/// Elements which are "empty" (as in empty list) or optional and not present are not serialized.
|
|
||||||
///
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct DeserVcard {
|
pub struct DeserVcard {
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
adr : Vec<String>,
|
adr : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
anniversary : Option<String>,
|
anniversary : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
bday : Option<String>,
|
bday : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
categories : Vec<String>,
|
categories : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
clientpidmap : Option<String>,
|
clientpidmap : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
email : Vec<String>,
|
email : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
fullname : Vec<String>,
|
fullname : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
gender : Option<String>,
|
gender : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
geo : Vec<String>,
|
geo : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
impp : Vec<String>,
|
impp : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
key : Vec<String>,
|
key : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
lang : Vec<String>,
|
lang : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
logo : Vec<String>,
|
logo : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
member : Vec<String>,
|
member : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
name : Option<String>,
|
name : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
nickname : Vec<String>,
|
nickname : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
note : Vec<String>,
|
note : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
org : Vec<String>,
|
org : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
photo : Vec<String>,
|
photo : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
proid : Option<String>,
|
proid : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
related : Vec<String>,
|
related : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
rev : Option<String>,
|
rev : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
role : Vec<String>,
|
role : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
sound : Vec<String>,
|
sound : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
tel : Vec<String>,
|
tel : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
title : Vec<String>,
|
title : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
tz : Vec<String>,
|
tz : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
uid : Option<String>,
|
uid : Option<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
#[serde(default)]
|
||||||
url : Vec<String>,
|
url : Vec<String>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(default)]
|
||||||
version : Option<String>
|
version : Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,3 +192,127 @@ impl From<Vcard> for DeserVcard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DeserVcard {
|
||||||
|
|
||||||
|
pub fn adr(&self) -> &Vec<String> {
|
||||||
|
&self.adr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn anniversary(&self) -> Option<&String> {
|
||||||
|
self.anniversary.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bday(&self) -> Option<&String> {
|
||||||
|
self.bday.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn categories(&self) -> &Vec<String> {
|
||||||
|
&self.categories
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clientpidmap(&self) -> Option<&String> {
|
||||||
|
self.clientpidmap.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn email(&self) -> &Vec<String> {
|
||||||
|
&self.email
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fullname(&self) -> &Vec<String> {
|
||||||
|
&self.fullname
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gender(&self) -> Option<&String> {
|
||||||
|
self.gender.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn geo(&self) -> &Vec<String> {
|
||||||
|
&self.geo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impp(&self) -> &Vec<String> {
|
||||||
|
&self.impp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key(&self) -> &Vec<String> {
|
||||||
|
&self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lang(&self) -> &Vec<String> {
|
||||||
|
&self.lang
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn logo(&self) -> &Vec<String> {
|
||||||
|
&self.logo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn member(&self) -> &Vec<String> {
|
||||||
|
&self.member
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Option<&String> {
|
||||||
|
self.name.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nickname(&self) -> &Vec<String> {
|
||||||
|
&self.nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn note(&self) -> &Vec<String> {
|
||||||
|
&self.note
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn org(&self) -> &Vec<String> {
|
||||||
|
&self.org
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn photo(&self) -> &Vec<String> {
|
||||||
|
&self.photo
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn proid(&self) -> Option<&String> {
|
||||||
|
self.proid.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn related(&self) -> &Vec<String> {
|
||||||
|
&self.related
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rev(&self) -> Option<&String> {
|
||||||
|
self.rev.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn role(&self) -> &Vec<String> {
|
||||||
|
&self.role
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sound(&self) -> &Vec<String> {
|
||||||
|
&self.sound
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tel(&self) -> &Vec<String> {
|
||||||
|
&self.tel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> &Vec<String> {
|
||||||
|
&self.title
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tz(&self) -> &Vec<String> {
|
||||||
|
&self.tz
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid(&self) -> Option<&String> {
|
||||||
|
self.uid.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn url(&self) -> &Vec<String> {
|
||||||
|
&self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(&self) -> Option<&String> {
|
||||||
|
self.version.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,14 @@ error_chain! {
|
||||||
|
|
||||||
links {
|
links {
|
||||||
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
||||||
RefError(::libimagentryref::error::RefError, ::libimagentryref::error::RefErrorKind);
|
|
||||||
VObjectError(::vobject::error::VObjectError, ::vobject::error::VObjectErrorKind);
|
VObjectError(::vobject::error::VObjectError, ::vobject::error::VObjectErrorKind);
|
||||||
EntryUtilError(::libimagentryutil::error::EntryUtilError, ::libimagentryutil::error::EntryUtilErrorKind);
|
EntryUtilError(::libimagentryutil::error::EntryUtilError, ::libimagentryutil::error::EntryUtilErrorKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreign_links {
|
foreign_links {
|
||||||
Io(::std::io::Error);
|
Io(::std::io::Error);
|
||||||
|
TomlDe(::toml::de::Error);
|
||||||
|
TomlSer(::toml::ser::Error);
|
||||||
TomlQueryError(::toml_query::error::Error);
|
TomlQueryError(::toml_query::error::Error);
|
||||||
UuidError(::uuid::ParseError);
|
UuidError(::uuid::ParseError);
|
||||||
}
|
}
|
||||||
|
@ -44,14 +45,19 @@ error_chain! {
|
||||||
display("Type error in header, expected {} at '{}', found other type", ty, loc)
|
display("Type error in header, expected {} at '{}', found other type", ty, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeaderDataMissing(datapath: &'static str) {
|
||||||
|
description("Data missing in header")
|
||||||
|
display("Data missing in header at '{}'", datapath)
|
||||||
|
}
|
||||||
|
|
||||||
EntryNotFound(sid: StoreId) {
|
EntryNotFound(sid: StoreId) {
|
||||||
description("Entry not found with StoreId")
|
description("Entry not found with StoreId")
|
||||||
display("Entry {:?} not found", sid)
|
display("Entry {:?} not found", sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
UidMissing(path: String) {
|
UidMissing(buf: String) {
|
||||||
description("Vcard object has no UID")
|
description("Vcard object has no UID")
|
||||||
display("Vcard at {:?} has no UID", path)
|
display("Vcard has no UID : {}", buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,6 @@ use error::Result;
|
||||||
pub struct ContactIter<'a>(StoreIdIterator, &'a Store);
|
pub struct ContactIter<'a>(StoreIdIterator, &'a Store);
|
||||||
|
|
||||||
/// Iterator over contacts
|
/// 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> {
|
impl<'a> ContactIter<'a> {
|
||||||
|
|
||||||
pub fn new(sii: StoreIdIterator, store: &'a Store) -> ContactIter<'a> {
|
pub fn new(sii: StoreIdIterator, store: &'a Store) -> ContactIter<'a> {
|
||||||
|
|
|
@ -33,16 +33,19 @@
|
||||||
while_true,
|
while_true,
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
#![recursion_limit="128"]
|
||||||
|
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
#[macro_use] extern crate error_chain;
|
#[macro_use] extern crate error_chain;
|
||||||
extern crate vobject;
|
extern crate vobject;
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
extern crate toml_query;
|
extern crate toml_query;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
extern crate serde;
|
||||||
|
#[macro_use] extern crate serde_derive;
|
||||||
|
|
||||||
#[macro_use] extern crate libimagstore;
|
#[macro_use] extern crate libimagstore;
|
||||||
extern crate libimagerror;
|
extern crate libimagerror;
|
||||||
extern crate libimagentryref;
|
|
||||||
#[macro_use] extern crate libimagentryutil;
|
#[macro_use] extern crate libimagentryutil;
|
||||||
|
|
||||||
module_entry_path_mod!("contact");
|
module_entry_path_mod!("contact");
|
||||||
|
@ -51,15 +54,6 @@ pub mod contact;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
pub mod deser;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
extern crate serde;
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[macro_use] extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(feature = "deser")]
|
|
||||||
pub mod deser;
|
|
||||||
|
|
||||||
|
|
|
@ -17,64 +17,38 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
//
|
//
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::result::Result as RResult;
|
|
||||||
|
|
||||||
use vobject::parse_component;
|
use toml::Value;
|
||||||
|
use toml::to_string as toml_to_string;
|
||||||
|
use toml::from_str as toml_from_str;
|
||||||
|
use toml_query::insert::TomlValueInsertExt;
|
||||||
|
use vobject::vcard::Vcard;
|
||||||
|
|
||||||
|
use libimagstore::storeid::IntoStoreId;
|
||||||
|
use libimagstore::storeid::StoreId;
|
||||||
use libimagstore::store::Store;
|
use libimagstore::store::Store;
|
||||||
use libimagstore::store::FileLockEntry;
|
use libimagstore::store::FileLockEntry;
|
||||||
use libimagstore::storeid::StoreIdIterator;
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
use libimagentryref::refstore::RefStore;
|
|
||||||
use libimagentryref::refstore::UniqueRefPathGenerator;
|
|
||||||
use libimagentryutil::isa::Is;
|
use libimagentryutil::isa::Is;
|
||||||
|
|
||||||
use contact::IsContact;
|
use contact::IsContact;
|
||||||
|
use deser::DeserVcard;
|
||||||
|
use module_path::ModuleEntryPath;
|
||||||
use error::ContactError as CE;
|
use error::ContactError as CE;
|
||||||
use error::ContactErrorKind as CEK;
|
use error::ContactErrorKind as CEK;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
pub struct UniqueContactPathGenerator;
|
pub trait ContactStore<'a> {
|
||||||
impl UniqueRefPathGenerator for UniqueContactPathGenerator {
|
|
||||||
type Error = CE;
|
|
||||||
|
|
||||||
/// The collection the `StoreId` should be created for
|
|
||||||
fn collection() -> &'static str {
|
|
||||||
"contact"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A function which should generate a unique string for a Path
|
|
||||||
fn unique_hash<A: AsRef<Path>>(path: A) -> RResult<String, Self::Error> {
|
|
||||||
use vobject::vcard::Vcard;
|
|
||||||
|
|
||||||
debug!("Generating unique hash for path: {:?}", path.as_ref());
|
|
||||||
util::read_to_string(path.as_ref())
|
|
||||||
.and_then(|s| Vcard::build(&s).map_err(CE::from))
|
|
||||||
.and_then(|card| {
|
|
||||||
card.uid()
|
|
||||||
.map(|u| u.raw().clone())
|
|
||||||
.ok_or_else(|| {
|
|
||||||
let s = path.as_ref().to_str().unwrap_or("Unknown path");
|
|
||||||
CEK::UidMissing(String::from(s)).into()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ContactStore<'a> : RefStore<'a> {
|
|
||||||
|
|
||||||
// creating
|
// creating
|
||||||
|
|
||||||
fn create_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>>;
|
fn create_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>>;
|
||||||
|
fn retrieve_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>>;
|
||||||
|
|
||||||
/// Create contact ref from buffer
|
fn create_from_buf(&'a self, buf: &str) -> Result<FileLockEntry<'a>>;
|
||||||
///
|
fn retrieve_from_buf(&'a self, buf: &str) -> Result<FileLockEntry<'a>>;
|
||||||
/// Needs the `p` argument as we're finally creating a reference by path, the buffer is only for
|
|
||||||
/// collecting metadata.
|
|
||||||
fn create_from_buf<P: AsRef<Path>>(&'a self, p: P, buf: &String) -> Result<FileLockEntry<'a>>;
|
|
||||||
|
|
||||||
// getting
|
// getting
|
||||||
|
|
||||||
|
@ -82,30 +56,25 @@ pub trait ContactStore<'a> : RefStore<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The extension for the Store to work with contacts
|
/// 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 {
|
impl<'a> ContactStore<'a> for Store {
|
||||||
|
|
||||||
fn create_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>> {
|
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))
|
util::read_to_string(p).and_then(|buf| self.create_from_buf(&buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>> {
|
||||||
|
util::read_to_string(p).and_then(|buf| self.retrieve_from_buf(&buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create contact ref from buffer
|
/// Create contact ref from buffer
|
||||||
fn create_from_buf<P: AsRef<Path>>(&'a self, p: P, buf: &String) -> Result<FileLockEntry<'a>> {
|
fn create_from_buf(&'a self, buf: &str) -> Result<FileLockEntry<'a>> {
|
||||||
let component = parse_component(&buf)?;
|
let (sid, value) = prepare_fetching_from_store(buf)?;
|
||||||
debug!("Parsed: {:?}", component);
|
postprocess_fetched_entry(self.create(sid)?, value)
|
||||||
|
}
|
||||||
|
|
||||||
RefStore::create_ref::<UniqueContactPathGenerator, P>(self, p)
|
fn retrieve_from_buf(&'a self, buf: &str) -> Result<FileLockEntry<'a>> {
|
||||||
.map_err(From::from)
|
let (sid, value) = prepare_fetching_from_store(buf)?;
|
||||||
.and_then(|mut entry| {
|
postprocess_fetched_entry(self.retrieve(sid)?, value)
|
||||||
entry.set_isflag::<IsContact>()
|
|
||||||
.map_err(From::from)
|
|
||||||
.map(|_| entry)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_contacts(&'a self) -> Result<StoreIdIterator> {
|
fn all_contacts(&'a self) -> Result<StoreIdIterator> {
|
||||||
|
@ -119,3 +88,30 @@ impl<'a> ContactStore<'a> for Store {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare the fetching from the store.
|
||||||
|
///
|
||||||
|
/// That means calculating the StoreId and the Value from the vcard data
|
||||||
|
fn prepare_fetching_from_store(buf: &str) -> Result<(StoreId, Value)> {
|
||||||
|
let vcard = Vcard::build(&buf)?;
|
||||||
|
debug!("Parsed: {:?}", vcard);
|
||||||
|
|
||||||
|
let uid = vcard.uid().ok_or_else(|| CE::from_kind(CEK::UidMissing(buf.to_string())))?;
|
||||||
|
|
||||||
|
let value = { // dirty ugly hack
|
||||||
|
let serialized = DeserVcard::from(vcard);
|
||||||
|
let serialized = toml_to_string(&serialized)?;
|
||||||
|
toml_from_str::<Value>(&serialized)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let sid = ModuleEntryPath::new(uid.raw()).into_storeid()?;
|
||||||
|
|
||||||
|
Ok((sid, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Postprocess the entry just fetched from the store
|
||||||
|
fn postprocess_fetched_entry<'a>(mut entry: FileLockEntry<'a>, value: Value) -> Result<FileLockEntry<'a>> {
|
||||||
|
entry.set_isflag::<IsContact>()?;
|
||||||
|
entry.get_header_mut().insert("contact.data", value)?;
|
||||||
|
Ok(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ use std::io::Read;
|
||||||
|
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
|
||||||
use vobject::Component;
|
|
||||||
|
|
||||||
pub fn read_to_string<A: AsRef<Path> + Debug>(pb: A) -> Result<String> {
|
pub fn read_to_string<A: AsRef<Path> + Debug>(pb: A) -> Result<String> {
|
||||||
let mut cont = String::new();
|
let mut cont = String::new();
|
||||||
|
|
||||||
|
@ -37,9 +35,3 @@ pub fn read_to_string<A: AsRef<Path> + Debug>(pb: A) -> Result<String> {
|
||||||
Ok(cont)
|
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue