diff --git a/bin/domain/imag-contact/Cargo.toml b/bin/domain/imag-contact/Cargo.toml index 53b4fb81..acaeb1bc 100644 --- a/bin/domain/imag-contact/Cargo.toml +++ b/bin/domain/imag-contact/Cargo.toml @@ -24,7 +24,6 @@ maintenance = { status = "actively-developed" } [dependencies] log = "0.4.0" toml = "0.4" -toml-query = "0.8" vobject = "0.7" handlebars = "1.0" walkdir = "2" @@ -46,3 +45,9 @@ version = "^2.29" default-features = false features = ["color", "suggestions", "wrap_help"] +[dependencies.toml-query] +#version = "0.8" +default-features = false +features = ["typed"] +git = "https://github.com/matthiasbeyer/toml-query" +branch = "master" diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs index 622a2ccb..4144279e 100644 --- a/bin/domain/imag-contact/src/create.rs +++ b/bin/domain/imag-contact/src/create.rs @@ -43,9 +43,11 @@ use vobject::vcard::Vcard; use vobject::vcard::VcardBuilder; use vobject::write_component; use toml_query::read::TomlValueReadExt; +use toml_query::read::Partial; use toml::Value; use uuid::Uuid; use failure::Error; +use failure::err_msg; use libimagcontact::store::ContactStore; use libimagrt::runtime::Runtime; @@ -80,8 +82,20 @@ fn ask_continue(inputstream: &mut Read, outputstream: &mut Write) -> bool { } pub fn create(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("create").unwrap(); - let mut template = String::from(TEMPLATE); + let scmd = rt.cli().subcommand_matches("create").unwrap(); + let mut template = String::from(TEMPLATE); + let collection_name = rt.cli().value_of("ref-collection-name").unwrap_or("contacts"); + let collection_name = String::from(collection_name); + let ref_config = rt // TODO: Re-Deserialize to libimagentryref::reference::Config + .config() + .ok_or_else(|| err_msg("Configuration missing, cannot continue!")) + .map_err_trace_exit_unwrap() + .read_partial::() + .map_err(Error::from) + .map_err_trace_exit_unwrap() + .ok_or_else(|| format_err!("Configuration missing: {}", libimagentryref::reference::Config::LOCATION)) + .map_err_trace_exit_unwrap(); + // TODO: Refactor the above to libimagutil or libimagrt? let (mut dest, location, uuid) : (Box, Option, String) = { if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) { @@ -202,7 +216,7 @@ pub fn create(rt: &Runtime) { if let Some(location) = location { if !scmd.is_present("dont-track") { let entry = rt.store() - .create_from_path(&location) + .create_from_path(&location, &ref_config, &collection_name) .map_err_trace_exit_unwrap(); let _ = rt.report_touched(entry.get_location()).unwrap_or_exit(); diff --git a/bin/domain/imag-contact/src/main.rs b/bin/domain/imag-contact/src/main.rs index 39c68ddf..821d271f 100644 --- a/bin/domain/imag-contact/src/main.rs +++ b/bin/domain/imag-contact/src/main.rs @@ -43,7 +43,7 @@ extern crate handlebars; extern crate walkdir; extern crate uuid; extern crate serde_json; -extern crate failure; +#[macro_use] extern crate failure; extern crate libimagcontact; extern crate libimagstore; @@ -59,7 +59,9 @@ use std::io::Write; use handlebars::Handlebars; use clap::ArgMatches; +use toml_query::read::TomlValueReadExt; use toml_query::read::TomlValueReadTypeExt; +use toml_query::read::Partial; use walkdir::WalkDir; use failure::Error; use failure::err_msg; @@ -158,6 +160,18 @@ fn import(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("import").unwrap(); // secured by main let path = scmd.value_of("path").map(PathBuf::from).unwrap(); // secured by clap + let collection_name = rt.cli().value_of("contact-ref-collection-name").unwrap(); // default by clap + let ref_config = rt.config() + .ok_or_else(|| format_err!("No configuration, cannot continue!")) + .map_err_trace_exit_unwrap() + .read_partial::() + .map_err(Error::from) + .map_err_trace_exit_unwrap() + .ok_or_else(|| format_err!("Configuration missing: {}", libimagentryref::reference::Config::LOCATION)) + .map_err_trace_exit_unwrap(); + // TODO: Refactor the above to libimagutil or libimagrt? + + if !path.exists() { error!("Path does not exist"); exit(1) @@ -166,7 +180,7 @@ fn import(rt: &Runtime) { if path.is_file() { let entry = rt .store() - .retrieve_from_path(&path) + .retrieve_from_path(&path, &ref_config, &collection_name) .map_err_trace_exit_unwrap(); let _ = rt.report_touched(entry.get_location()).unwrap_or_exit(); @@ -180,7 +194,7 @@ fn import(rt: &Runtime) { let pb = PathBuf::from(entry.path()); let fle = rt .store() - .retrieve_from_path(&pb) + .retrieve_from_path(&pb, &ref_config, &collection_name) .map_err_trace_exit_unwrap(); let _ = rt.report_touched(fle.get_location()).unwrap_or_exit(); diff --git a/bin/domain/imag-contact/src/ui.rs b/bin/domain/imag-contact/src/ui.rs index 5b939422..4a0b1095 100644 --- a/bin/domain/imag-contact/src/ui.rs +++ b/bin/domain/imag-contact/src/ui.rs @@ -21,6 +21,14 @@ use clap::{Arg, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app + .arg(Arg::with_name("contact-ref-collection-name") + .long("ref-collection") + .takes_value(true) + .required(false) + .multiple(false) + .default_value("contacts") + .help("Name (Key) of the basepath setting in the configuration file to use")) + .subcommand(SubCommand::with_name("list") .about("List contacts") .version("0.1") @@ -56,6 +64,7 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .multiple(false) .value_name("PATH") .help("Import from this file/directory")) + ) .subcommand(SubCommand::with_name("show") diff --git a/imagrc.toml b/imagrc.toml index f1d3be91..65271002 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -356,6 +356,7 @@ execute_in_store = false [ref.basepathes] music = "/home/user/music" mail = "/home/user/mail" +contacts = "/home/user/contacts" [mail] # The name of the mail reference collection diff --git a/lib/domain/libimagcontact/Cargo.toml b/lib/domain/libimagcontact/Cargo.toml index 36f2f989..b63b22fe 100644 --- a/lib/domain/libimagcontact/Cargo.toml +++ b/lib/domain/libimagcontact/Cargo.toml @@ -31,5 +31,6 @@ serde_derive = "1" libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } -libimagentryutil = { version = "0.10.0", path = "../../../lib/entry/libimagentryutil/" } +libimagentryutil = { version = "0.10.0", path = "../../../lib/entry/libimagentryutil/" } +libimagentryref = { version = "0.10.0", path = "../../../lib/entry/libimagentryref/" } diff --git a/lib/domain/libimagcontact/src/lib.rs b/lib/domain/libimagcontact/src/lib.rs index 32eaffdd..0dc57e10 100644 --- a/lib/domain/libimagcontact/src/lib.rs +++ b/lib/domain/libimagcontact/src/lib.rs @@ -48,6 +48,7 @@ extern crate serde; #[macro_use] extern crate libimagstore; extern crate libimagerror; +extern crate libimagentryref; #[macro_use] extern crate libimagentryutil; module_entry_path_mod!("contact"); diff --git a/lib/domain/libimagcontact/src/store.rs b/lib/domain/libimagcontact/src/store.rs index 7c877832..3fd3771d 100644 --- a/lib/domain/libimagcontact/src/store.rs +++ b/lib/domain/libimagcontact/src/store.rs @@ -18,6 +18,7 @@ // use std::path::PathBuf; +use std::path::Path; use toml::Value; use toml::to_string as toml_to_string; @@ -32,6 +33,7 @@ use libimagstore::iter::Entries; use libimagstore::store::Store; use libimagstore::store::FileLockEntry; use libimagentryutil::isa::Is; +use libimagentryref::reference::{MutRef, Config as RefConfig}; use contact::IsContact; use deser::DeserVcard; @@ -39,13 +41,23 @@ use util; pub trait ContactStore<'a> { - // creating + fn create_from_path(&'a self, p: &PathBuf, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef; - fn create_from_path(&'a self, p: &PathBuf) -> Result>; - fn retrieve_from_path(&'a self, p: &PathBuf) -> Result>; + fn retrieve_from_path(&'a self, p: &PathBuf, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef; - fn create_from_buf(&'a self, buf: &str) -> Result>; - fn retrieve_from_buf(&'a self, buf: &str) -> Result>; + fn create_from_buf(&'a self, buf: &str, path: P, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef, + P: AsRef; + + fn retrieve_from_buf(&'a self, buf: &str, path: P, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef, + P: AsRef; // getting @@ -55,23 +67,58 @@ pub trait ContactStore<'a> { /// The extension for the Store to work with contacts 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(&buf)) + /// Create a contact from a filepath + /// + /// Uses the collection with `collection_name` from RefConfig to store the reference to the + /// file. + fn create_from_path(&'a self, p: &PathBuf, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef + { + util::read_to_string(p).and_then(|buf| self.create_from_buf(&buf, p, rc, collection_name)) } - fn retrieve_from_path(&'a self, p: &PathBuf) -> Result> { - util::read_to_string(p).and_then(|buf| self.retrieve_from_buf(&buf)) + /// Retrieve a contact from a filepath + /// + /// Uses the collection with `collection_name` from RefConfig to store the reference to the + /// file. + fn retrieve_from_path(&'a self, p: &PathBuf, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef + { + util::read_to_string(p).and_then(|buf| self.retrieve_from_buf(&buf, p, rc, collection_name)) } - /// Create contact ref from buffer - fn create_from_buf(&'a self, buf: &str) -> Result> { + /// Create a contact from a buffer + /// + /// Uses the collection with `collection_name` from RefConfig to store the reference to the + /// file. + /// + /// Needs the `path` passed where the buffer was read from, because we want to create a + /// reference to it. + fn create_from_buf(&'a self, buf: &str, path: P, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef, + P: AsRef + { let (sid, value) = prepare_fetching_from_store(buf)?; - postprocess_fetched_entry(self.create(sid)?, value) + postprocess_fetched_entry(self.create(sid)?, value, path, rc, collection_name) } - fn retrieve_from_buf(&'a self, buf: &str) -> Result> { + /// Retrieve a contact from a buffer + /// + /// Uses the collection with `collection_name` from RefConfig to store the reference to the + /// file. + /// + /// Needs the `path` passed where the buffer was read from, because we want to create a + /// reference to it. + fn retrieve_from_buf(&'a self, buf: &str, path: P, rc: &RefConfig, collection_name: CN) + -> Result> + where CN: AsRef, + P: AsRef + { let (sid, value) = prepare_fetching_from_store(buf)?; - postprocess_fetched_entry(self.retrieve(sid)?, value) + postprocess_fetched_entry(self.retrieve(sid)?, value, path, rc, collection_name) } fn all_contacts(&'a self) -> Result> { @@ -101,10 +148,29 @@ fn prepare_fetching_from_store(buf: &str) -> Result<(StoreId, Value)> { Ok((sid, value)) } -/// Postprocess the entry just fetched from the store -fn postprocess_fetched_entry<'a>(mut entry: FileLockEntry<'a>, value: Value) -> Result> { +/// Postprocess the entry just fetched (created or retrieved) from the store +/// +/// We need the path, the refconfig and the collection name passed here because we create a +/// reference here. +/// +/// This is marked as inline because what it does is trivial, but repetitve in this module. +#[inline] +fn postprocess_fetched_entry<'a, CN, P>(mut entry: FileLockEntry<'a>, + value: Value, + path: P, + rc: &RefConfig, + collection_name: CN) + -> Result> + where CN: AsRef, + P: AsRef +{ + use libimagentryref::reference::RefFassade; + use libimagentryref::hasher::sha1::Sha1Hasher; + entry.set_isflag::()?; entry.get_header_mut().insert("contact.data", value)?; + entry.as_ref_with_hasher_mut::().make_ref(path, collection_name, rc, false)?; + Ok(entry) }