diff --git a/bin/domain/imag-contact/Cargo.toml b/bin/domain/imag-contact/Cargo.toml index 5986b77c..eec3521c 100644 --- a/bin/domain/imag-contact/Cargo.toml +++ b/bin/domain/imag-contact/Cargo.toml @@ -38,12 +38,7 @@ libimagutil = { version = "0.8.0", path = "../../../lib/etc/libimagutil" libimagentryref = { version = "0.8.0", path = "../../../lib/entry/libimagentryref" } libimagentryedit = { version = "0.8.0", path = "../../../lib/entry/libimagentryedit" } libimaginteraction = { version = "0.8.0", path = "../../../lib/etc/libimaginteraction" } - -[dependencies.libimagcontact] -version = "0.8.0" -path = "../../../lib/domain/libimagcontact" -default-features = false -features = ["deser"] +libimagcontact = { version = "0.8.0", path = "../../../lib/domain/libimagcontact" } [dependencies.clap] version = "^2.29" diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs index 15d885b2..a9573c11 100644 --- a/bin/domain/imag-contact/src/create.rs +++ b/bin/domain/imag-contact/src/create.rs @@ -44,14 +44,13 @@ use toml_query::read::TomlValueReadExt; use toml::Value; use uuid::Uuid; +use libimagcontact::store::ContactStore; use libimagcontact::error::ContactError as CE; -use libimagcontact::store::UniqueContactPathGenerator; use libimagrt::runtime::Runtime; use libimagerror::str::ErrFromStr; use libimagerror::trace::MapErrTrace; use libimagerror::trace::trace_error; use libimagutil::warn_result::WarnResult; -use libimagentryref::refstore::RefStore; 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 !scmd.is_present("dont-track") { - RefStore::create_ref::(rt.store(), location) + rt.store() + .create_from_path(&location) .map_err_trace_exit_unwrap(1); info!("Created entry in store"); diff --git a/bin/domain/imag-contact/src/main.rs b/bin/domain/imag-contact/src/main.rs index 031c2169..86c98457 100644 --- a/bin/domain/imag-contact/src/main.rs +++ b/bin/domain/imag-contact/src/main.rs @@ -48,7 +48,6 @@ extern crate libimagstore; extern crate libimagerror; extern crate libimagutil; extern crate libimaginteraction; -extern crate libimagentryref; extern crate libimagentryedit; use std::process::exit; @@ -57,7 +56,6 @@ use std::io::Write; use handlebars::Handlebars; use clap::ArgMatches; -use vobject::vcard::Vcard; use toml_query::read::TomlValueReadTypeExt; use walkdir::WalkDir; @@ -67,14 +65,12 @@ use libimagerror::str::ErrFromStr; use libimagerror::trace::MapErrTrace; use libimagerror::io::ToExitCode; use libimagerror::exit::ExitUnwrap; +use libimagerror::iter::TraceIterator; use libimagcontact::store::ContactStore; -use libimagcontact::store::UniqueContactPathGenerator; use libimagcontact::error::ContactError as CE; use libimagcontact::contact::Contact; use libimagcontact::deser::DeserVcard; use libimagstore::iter::get::StoreIdGetIteratorExtension; -use libimagentryref::reference::Ref; -use libimagentryref::refstore::RefStore; mod ui; mod util; @@ -128,24 +124,13 @@ fn list(rt: &Runtime) { .ok_or_else(|| CE::from("StoreId not found".to_owned())) .map_err_trace_exit_unwrap(1); - fle - .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) + fle.deser().map_err_trace_exit_unwrap(1) }) .enumerate(); if scmd.is_present("json") { - let v : Vec = iterator - .map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect(); + let v : Vec = iterator.map(|tpl| tpl.1).collect(); + match ::serde_json::to_string(&v) { Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(), Err(e) => { @@ -155,9 +140,8 @@ fn list(rt: &Runtime) { } } else { iterator - .map(|(i, (fle, vcard))| { - let hash = String::from(fle.get_hash().map_err_trace_exit_unwrap(1)); - let data = build_data_object_for_handlebars(i, hash, &vcard); + .map(|(i, deservcard)| { + let data = build_data_object_for_handlebars(i, &deservcard); list_format.render("format", &data) .err_from_str() @@ -217,32 +201,47 @@ fn import(rt: &Runtime) { } fn show(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("show").unwrap(); - let hash = scmd.value_of("hash").map(String::from).unwrap(); // safed by clap - - let contact_data = rt.store() - .get_ref::(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 scmd = rt.cli().subcommand_matches("show").unwrap(); + let hash = scmd.value_of("hash").map(String::from).unwrap(); // safed by clap 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 - .render("format", &data) - .err_from_str() - .map_err(CE::from) - .map_err_trace_exit_unwrap(1); - let _ = writeln!(::std::io::stdout(), "{}", s).to_exit_code().unwrap_or_exit(); + rt.store() + .all_contacts() + .map_err_trace_exit_unwrap(1) + .into_get_iter(rt.store()) + .trace_unwrap_exit(1) + .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) { @@ -270,30 +269,20 @@ fn find(rt: &Runtime) { }) .unwrap() // safed above }) - .filter_map(|cont| { - let comp = cont - .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 + .filter_map(|entry| { + let card = entry.deser().map_err_trace_exit_unwrap(1); let str_contains_any = |s: &String, v: &Vec| { v.iter().any(|i| s.contains(i)) }; - let take = card.adr().iter().any(|a| str_contains_any(a.raw(), &grepstring)) - || card.email().iter().any(|a| str_contains_any(a.raw(), &grepstring)) - || card.fullname().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, &grepstring)) + || card.fullname().iter().any(|a| str_contains_any(a, &grepstring)); if take { // optimization so we don't have to parse again in the next step - Some((cont, card)) + Some((entry, card)) } else { None } @@ -301,8 +290,8 @@ fn find(rt: &Runtime) { .enumerate(); if scmd.is_present("json") { - let v : Vec = iterator - .map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect(); + let v : Vec = iterator.map(|(_, tlp)| tlp.1).collect(); + match ::serde_json::to_string(&v) { Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(), Err(e) => { @@ -312,22 +301,22 @@ fn find(rt: &Runtime) { } } else if scmd.is_present("find-id") { iterator - .for_each(|(_i, (fle, _card))| { - writeln!(rt.stdout(), "{}", fle.get_location()) + .for_each(|(_i, (entry, _))| { + writeln!(rt.stdout(), "{}", entry.get_location()) .to_exit_code() .unwrap_or_exit(); }) } else if scmd.is_present("find-full-id") { let storepath = rt.store().path().display(); iterator - .for_each(|(_i, (fle, _card))| { - writeln!(rt.stdout(), "{}/{}", storepath, fle.get_location()) + .for_each(|(_i, (entry, _))| { + writeln!(rt.stdout(), "{}/{}", storepath, entry.get_location()) .to_exit_code() .unwrap_or_exit(); }) } else { iterator - .for_each(|(i, (fle, card))| { + .for_each(|(i, (_, card))| { let fmt = if scmd.is_present("find-show") { &show_format } else if scmd.is_present("find-list") { @@ -336,8 +325,7 @@ fn find(rt: &Runtime) { &list_format }; - let hash = fle.get_hash().map(String::from).map_err_trace_exit_unwrap(1); - let data = build_data_object_for_handlebars(i, hash, &card); + let data = build_data_object_for_handlebars(i, &card); let s = fmt .render("format", &data) .err_from_str() diff --git a/bin/domain/imag-contact/src/util.rs b/bin/domain/imag-contact/src/util.rs index 12730a91..91e00783 100644 --- a/bin/domain/imag-contact/src/util.rs +++ b/bin/domain/imag-contact/src/util.rs @@ -18,105 +18,58 @@ // 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 process_list = |list: &Vec| { + list.iter() + .map(String::clone) + .collect::>() + .join(", ") + }; + + let process_opt = |opt: Option<&String>| { + opt.map(String::clone).unwrap_or_else(String::new) + }; + { data.insert("i" , format!("{}", i)); // The hash (as in libimagentryref) of the contact - data.insert("id" , hash); - - data.insert("ADR" , vcard.adr() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("ANNIVERSARY" , vcard.anniversary() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("BDAY" , vcard.bday() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("CATEGORIES" , vcard.categories() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("CLIENTPIDMAP" , vcard.clientpidmap() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("EMAIL" , vcard.email() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("FN" , vcard.fullname() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("GENDER" , vcard.gender() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("GEO" , vcard.geo() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("IMPP" , vcard.impp() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("KEY" , vcard.key() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("LANG" , vcard.lang() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("LOGO" , vcard.logo() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("MEMBER" , vcard.member() - .into_iter().map(|c| c.raw().clone()).collect::>().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::>().join(", ")); - - data.insert("NOTE" , vcard.note() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("ORG" , vcard.org() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("PHOTO" , vcard.photo() - .into_iter().map(|c| c.raw().clone()).collect::>().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::>().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::>().join(", ")); - - data.insert("SOUND" , vcard.sound() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("TEL" , vcard.tel() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("TITLE" , vcard.title() - .into_iter().map(|c| c.raw().clone()).collect::>().join(", ")); - - data.insert("TZ" , vcard.tz() - .into_iter().map(|c| c.raw().clone()).collect::>().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::>().join(", ")); - - data.insert("VERSION" , vcard.version() - .map(|c| c.raw().clone()).unwrap_or(String::new())); + data.insert("id" , process_opt(vcard.uid())); + data.insert("ADR" , process_list(vcard.adr())); + data.insert("ANNIVERSARY" , process_opt(vcard.anniversary())); + data.insert("BDAY" , process_opt(vcard.bday())); + data.insert("CATEGORIES" , process_list(vcard.categories())); + data.insert("CLIENTPIDMAP" , process_opt(vcard.clientpidmap())); + data.insert("EMAIL" , process_list(vcard.email())); + data.insert("FN" , process_list(vcard.fullname())); + data.insert("GENDER" , process_opt(vcard.gender())); + data.insert("GEO" , process_list(vcard.geo())); + data.insert("IMPP" , process_list(vcard.impp())); + data.insert("KEY" , process_list(vcard.key())); + data.insert("LANG" , process_list(vcard.lang())); + data.insert("LOGO" , process_list(vcard.logo())); + data.insert("MEMBER" , process_list(vcard.member())); + data.insert("N" , process_opt(vcard.name())); + data.insert("NICKNAME" , process_list(vcard.nickname())); + data.insert("NOTE" , process_list(vcard.note())); + data.insert("ORG" , process_list(vcard.org())); + data.insert("PHOTO" , process_list(vcard.photo())); + data.insert("PRIOD" , process_opt(vcard.proid())); + data.insert("RELATED" , process_list(vcard.related())); + data.insert("REV" , process_opt(vcard.rev())); + data.insert("ROLE" , process_list(vcard.role())); + data.insert("SOUND" , process_list(vcard.sound())); + data.insert("TEL" , process_list(vcard.tel())); + data.insert("TITLE" , process_list(vcard.title())); + data.insert("TZ" , process_list(vcard.tz())); + data.insert("UID" , process_opt(vcard.uid())); + data.insert("URL" , process_list(vcard.url())); + data.insert("VERSION" , process_opt(vcard.version())); } data diff --git a/lib/domain/libimagcontact/Cargo.toml b/lib/domain/libimagcontact/Cargo.toml index 84ea6c03..d295248a 100644 --- a/lib/domain/libimagcontact/Cargo.toml +++ b/lib/domain/libimagcontact/Cargo.toml @@ -25,21 +25,11 @@ log = "0.3" toml = "0.4" toml-query = "0.6" vobject = "0.4" -uuid = { version = "0.6", features = ["v4"] } -serde = { version = "1", optional = true } -serde_derive = { version = "1", optional = true } +uuid = "0.6" +serde = "1" +serde_derive = "1" libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" } libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" } 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"] - diff --git a/lib/domain/libimagcontact/src/contact.rs b/lib/domain/libimagcontact/src/contact.rs index 375eeed4..fb586458 100644 --- a/lib/domain/libimagcontact/src/contact.rs +++ b/lib/domain/libimagcontact/src/contact.rs @@ -17,28 +17,27 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use std::ops::Deref; - -use vobject::Component; +use toml::to_string as toml_to_string; +use toml::from_str as toml_from_str; +use toml_query::read::TomlValueReadExt; use libimagstore::store::Entry; -use libimagentryref::reference::Ref; use libimagentryutil::isa::Is; use libimagentryutil::isa::IsKindHeaderPathProvider; +use deser::DeserVcard; use error::Result; -use util; +use error::ContactError as CE; +use error::ContactErrorKind as CEK; /// 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 { +pub trait Contact { fn is_contact(&self) -> Result; // getting data - fn get_contact_data(&self) -> Result; + fn deser(&self) -> Result; // More convenience functionality may follow @@ -52,34 +51,18 @@ impl Contact for Entry { self.is::().map_err(From::from) } - fn get_contact_data(&self) -> Result { - let component = self - .get_path() - .map_err(From::from) - .and_then(util::read_to_string) - .and_then(util::parse)?; + fn deser(&self) -> Result { + let data = self + .get_header() + .read("contact.data")? + .ok_or_else(|| CE::from_kind(CEK::HeaderDataMissing("contact.data")))?; - 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 - } -} - - diff --git a/lib/domain/libimagcontact/src/deser.rs b/lib/domain/libimagcontact/src/deser.rs index 84855cd9..aa1dc806 100644 --- a/lib/domain/libimagcontact/src/deser.rs +++ b/lib/domain/libimagcontact/src/deser.rs @@ -20,103 +20,127 @@ use vobject::vcard::Vcard; /// A type which can be build from a Vcard and be serialized. -/// -/// # 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)] +#[derive(Serialize, Deserialize, Debug)] pub struct DeserVcard { #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] adr : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] anniversary : Option, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] bday : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] categories : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] clientpidmap : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] email : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] fullname : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] gender : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] geo : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] impp : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] key : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] lang : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] logo : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] member : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] name : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] nickname : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] note : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] org : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] photo : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] proid : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] related : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] rev : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] role : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] sound : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] tel : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] title : Vec, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] tz : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] uid : Option, #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] url : Vec, #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] version : Option } @@ -168,3 +192,127 @@ impl From for DeserVcard { } } +impl DeserVcard { + + pub fn adr(&self) -> &Vec { + &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 { + &self.categories + } + + pub fn clientpidmap(&self) -> Option<&String> { + self.clientpidmap.as_ref() + } + + pub fn email(&self) -> &Vec { + &self.email + } + + pub fn fullname(&self) -> &Vec { + &self.fullname + } + + pub fn gender(&self) -> Option<&String> { + self.gender.as_ref() + } + + pub fn geo(&self) -> &Vec { + &self.geo + } + + pub fn impp(&self) -> &Vec { + &self.impp + } + + pub fn key(&self) -> &Vec { + &self.key + } + + pub fn lang(&self) -> &Vec { + &self.lang + } + + pub fn logo(&self) -> &Vec { + &self.logo + } + + pub fn member(&self) -> &Vec { + &self.member + } + + pub fn name(&self) -> Option<&String> { + self.name.as_ref() + } + + pub fn nickname(&self) -> &Vec { + &self.nickname + } + + pub fn note(&self) -> &Vec { + &self.note + } + + pub fn org(&self) -> &Vec { + &self.org + } + + pub fn photo(&self) -> &Vec { + &self.photo + } + + pub fn proid(&self) -> Option<&String> { + self.proid.as_ref() + } + + pub fn related(&self) -> &Vec { + &self.related + } + + pub fn rev(&self) -> Option<&String> { + self.rev.as_ref() + } + + pub fn role(&self) -> &Vec { + &self.role + } + + pub fn sound(&self) -> &Vec { + &self.sound + } + + pub fn tel(&self) -> &Vec { + &self.tel + } + + pub fn title(&self) -> &Vec { + &self.title + } + + pub fn tz(&self) -> &Vec { + &self.tz + } + + pub fn uid(&self) -> Option<&String> { + self.uid.as_ref() + } + + pub fn url(&self) -> &Vec { + &self.url + } + + pub fn version(&self) -> Option<&String> { + self.version.as_ref() + } + +} + diff --git a/lib/domain/libimagcontact/src/error.rs b/lib/domain/libimagcontact/src/error.rs index 37ff4c26..8eb757b5 100644 --- a/lib/domain/libimagcontact/src/error.rs +++ b/lib/domain/libimagcontact/src/error.rs @@ -26,13 +26,14 @@ error_chain! { links { StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind); - RefError(::libimagentryref::error::RefError, ::libimagentryref::error::RefErrorKind); VObjectError(::vobject::error::VObjectError, ::vobject::error::VObjectErrorKind); EntryUtilError(::libimagentryutil::error::EntryUtilError, ::libimagentryutil::error::EntryUtilErrorKind); } foreign_links { Io(::std::io::Error); + TomlDe(::toml::de::Error); + TomlSer(::toml::ser::Error); TomlQueryError(::toml_query::error::Error); UuidError(::uuid::ParseError); } @@ -44,14 +45,19 @@ error_chain! { 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) { description("Entry not found with StoreId") display("Entry {:?} not found", sid) } - UidMissing(path: String) { + UidMissing(buf: String) { description("Vcard object has no UID") - display("Vcard at {:?} has no UID", path) + display("Vcard has no UID : {}", buf) } } diff --git a/lib/domain/libimagcontact/src/iter.rs b/lib/domain/libimagcontact/src/iter.rs index 8e66c833..1f99c873 100644 --- a/lib/domain/libimagcontact/src/iter.rs +++ b/lib/domain/libimagcontact/src/iter.rs @@ -29,13 +29,6 @@ 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> { diff --git a/lib/domain/libimagcontact/src/lib.rs b/lib/domain/libimagcontact/src/lib.rs index 6e55e570..87b244e2 100644 --- a/lib/domain/libimagcontact/src/lib.rs +++ b/lib/domain/libimagcontact/src/lib.rs @@ -33,16 +33,19 @@ while_true, )] +#![recursion_limit="128"] + #[macro_use] extern crate log; #[macro_use] extern crate error_chain; extern crate vobject; extern crate toml; extern crate toml_query; extern crate uuid; +extern crate serde; +#[macro_use] extern crate serde_derive; #[macro_use] extern crate libimagstore; extern crate libimagerror; -extern crate libimagentryref; #[macro_use] extern crate libimagentryutil; module_entry_path_mod!("contact"); @@ -51,15 +54,6 @@ pub mod contact; pub mod error; pub mod iter; pub mod store; +pub mod deser; mod util; - -#[cfg(feature = "serde")] -extern crate serde; - -#[cfg(feature = "serde")] -#[macro_use] extern crate serde_derive; - -#[cfg(feature = "deser")] -pub mod deser; - diff --git a/lib/domain/libimagcontact/src/store.rs b/lib/domain/libimagcontact/src/store.rs index 02ac57c2..88856b70 100644 --- a/lib/domain/libimagcontact/src/store.rs +++ b/lib/domain/libimagcontact/src/store.rs @@ -17,64 +17,38 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use std::path::Path; 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::FileLockEntry; use libimagstore::storeid::StoreIdIterator; -use libimagentryref::refstore::RefStore; -use libimagentryref::refstore::UniqueRefPathGenerator; use libimagentryutil::isa::Is; use contact::IsContact; +use deser::DeserVcard; +use module_path::ModuleEntryPath; use error::ContactError as CE; use error::ContactErrorKind as CEK; use error::Result; use util; -pub struct UniqueContactPathGenerator; -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>(path: A) -> RResult { - 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> { +pub trait ContactStore<'a> { // creating - fn create_from_path(&'a self, p: &PathBuf) -> Result>; + fn create_from_path(&'a self, p: &PathBuf) -> Result>; + fn retrieve_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: P, buf: &String) -> Result>; + fn create_from_buf(&'a self, buf: &str) -> Result>; + fn retrieve_from_buf(&'a self, buf: &str) -> Result>; // getting @@ -82,30 +56,25 @@ pub trait ContactStore<'a> : RefStore<'a> { } /// 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)) + util::read_to_string(p).and_then(|buf| self.create_from_buf(&buf)) + } + + fn retrieve_from_path(&'a self, p: &PathBuf) -> Result> { + util::read_to_string(p).and_then(|buf| self.retrieve_from_buf(&buf)) } /// Create contact ref from buffer - fn create_from_buf>(&'a self, p: P, buf: &String) -> Result> { - let component = parse_component(&buf)?; - debug!("Parsed: {:?}", component); + fn create_from_buf(&'a self, buf: &str) -> Result> { + let (sid, value) = prepare_fetching_from_store(buf)?; + postprocess_fetched_entry(self.create(sid)?, value) + } - RefStore::create_ref::(self, p) - .map_err(From::from) - .and_then(|mut entry| { - entry.set_isflag::() - .map_err(From::from) - .map(|_| entry) - }) + fn retrieve_from_buf(&'a self, buf: &str) -> Result> { + let (sid, value) = prepare_fetching_from_store(buf)?; + postprocess_fetched_entry(self.retrieve(sid)?, value) } fn all_contacts(&'a self) -> Result { @@ -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::(&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> { + entry.set_isflag::()?; + entry.get_header_mut().insert("contact.data", value)?; + Ok(entry) +} + diff --git a/lib/domain/libimagcontact/src/util.rs b/lib/domain/libimagcontact/src/util.rs index 8a72b872..53e820c4 100644 --- a/lib/domain/libimagcontact/src/util.rs +++ b/lib/domain/libimagcontact/src/util.rs @@ -24,8 +24,6 @@ use std::io::Read; use error::Result; -use vobject::Component; - pub fn read_to_string + Debug>(pb: A) -> Result { let mut cont = String::new(); @@ -37,9 +35,3 @@ pub fn read_to_string + Debug>(pb: A) -> Result { Ok(cont) } -/// Helper for chaining results nicely -pub fn parse(buf: String) -> Result { - use vobject::parse_component; - parse_component(&buf).map_err(From::from) -} -