2017-10-18 09:20:27 +00:00
|
|
|
//
|
|
|
|
// imag - the personal information management suite for the commandline
|
2019-01-03 01:32:07 +00:00
|
|
|
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors
|
2017-10-18 09:20:27 +00:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
|
2018-04-13 22:39:43 +00:00
|
|
|
#![deny(
|
|
|
|
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,
|
|
|
|
)]
|
|
|
|
|
2017-10-18 09:20:27 +00:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use std::process::exit;
|
2018-11-01 19:33:24 +00:00
|
|
|
use std::io::Read;
|
2017-10-18 09:20:27 +00:00
|
|
|
use std::io::Write;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::fs::OpenOptions;
|
|
|
|
|
|
|
|
use vobject::vcard::Vcard;
|
2018-09-27 12:42:58 +00:00
|
|
|
use vobject::vcard::VcardBuilder;
|
2017-10-18 09:20:27 +00:00
|
|
|
use vobject::write_component;
|
|
|
|
use toml_query::read::TomlValueReadExt;
|
2019-04-16 16:33:02 +00:00
|
|
|
use toml_query::read::Partial;
|
2017-10-18 09:20:27 +00:00
|
|
|
use toml::Value;
|
|
|
|
use uuid::Uuid;
|
2018-10-30 17:40:53 +00:00
|
|
|
use failure::Error;
|
2019-04-16 16:33:02 +00:00
|
|
|
use failure::err_msg;
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-25 11:34:46 +00:00
|
|
|
use libimagcontact::store::ContactStore;
|
2017-10-18 09:20:27 +00:00
|
|
|
use libimagrt::runtime::Runtime;
|
|
|
|
use libimagerror::trace::MapErrTrace;
|
|
|
|
use libimagerror::trace::trace_error;
|
2019-02-05 00:37:32 +00:00
|
|
|
use libimagerror::exit::ExitUnwrap;
|
2017-10-18 09:20:27 +00:00
|
|
|
use libimagutil::warn_result::WarnResult;
|
|
|
|
|
2019-08-27 08:42:46 +00:00
|
|
|
const TEMPLATE : &str = include_str!("../static/new-contact-template.toml");
|
2017-10-18 09:20:27 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use toml::Value;
|
|
|
|
use super::TEMPLATE;
|
|
|
|
|
2019-08-27 08:42:46 +00:00
|
|
|
const TEMPLATE_WITH_DATA : &str = include_str!("../static/new-contact-template-test.toml");
|
2017-10-18 09:20:27 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validity_template_toml() {
|
|
|
|
let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_validity_template_toml_without_comments() {
|
|
|
|
let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-25 08:37:20 +00:00
|
|
|
fn ask_continue(inputstream: &mut dyn Read, outputstream: &mut dyn Write) -> bool {
|
2018-11-01 19:33:24 +00:00
|
|
|
::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true), inputstream, outputstream)
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap()
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create(rt: &Runtime) {
|
2019-04-16 16:33:02 +00:00
|
|
|
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::<libimagentryref::reference::Config>()
|
|
|
|
.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?
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2019-08-25 08:37:20 +00:00
|
|
|
let (mut dest, location, uuid) : (Box<dyn Write>, Option<PathBuf>, String) = {
|
2017-10-18 09:20:27 +00:00
|
|
|
if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uuid = if fl.is_file() {
|
2017-10-18 09:20:27 +00:00
|
|
|
error!("File does exist, cannot create/override");
|
2018-11-01 19:33:24 +00:00
|
|
|
exit(1)
|
2017-10-18 09:20:27 +00:00
|
|
|
} else if fl.is_dir() {
|
2018-09-30 13:11:36 +00:00
|
|
|
let uuid = Uuid::new_v4().to_hyphenated().to_string();
|
2018-04-26 12:15:30 +00:00
|
|
|
fl.push(uuid.clone());
|
2018-04-27 11:58:46 +00:00
|
|
|
fl.set_extension("vcf");
|
2017-11-08 16:21:57 +00:00
|
|
|
info!("Creating file: {:?}", fl);
|
2018-04-26 12:15:30 +00:00
|
|
|
Some(uuid)
|
|
|
|
} else {
|
2018-04-27 11:58:46 +00:00
|
|
|
let has_vcf_ext = fl
|
|
|
|
.extension()
|
|
|
|
.and_then(|ext| ext.to_str())
|
|
|
|
.map(|ext| ext == "vcf")
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
if !has_vcf_ext {
|
|
|
|
let f = fl.file_name()
|
|
|
|
.and_then(|ext| ext.to_str())
|
|
|
|
.map(|f| format!(" '{}' ", f)) // ugly
|
|
|
|
.unwrap_or_else(|| String::from(" ")); // hack
|
|
|
|
|
|
|
|
warn!("File{}has no extension 'vcf'", f); // ahead
|
|
|
|
warn!("other tools might not recognize this as contact.");
|
|
|
|
warn!("Continuing...");
|
|
|
|
}
|
|
|
|
|
2018-04-26 12:15:30 +00:00
|
|
|
None
|
|
|
|
};
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 16:21:57 +00:00
|
|
|
debug!("Destination = {:?}", fl);
|
|
|
|
|
2017-10-18 09:20:27 +00:00
|
|
|
let file = OpenOptions::new()
|
|
|
|
.write(true)
|
|
|
|
.create_new(true)
|
|
|
|
.open(fl.clone())
|
|
|
|
.map_warn_err_str("Cannot create/open destination File. Stopping.")
|
2018-10-30 17:40:53 +00:00
|
|
|
.map_err(Error::from)
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap();
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-26 12:15:30 +00:00
|
|
|
let uuid_string = uuid
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
fl.file_name()
|
|
|
|
.and_then(|fname| fname.to_str())
|
|
|
|
.map(String::from)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
error!("Cannot calculate UUID for vcard");
|
|
|
|
exit(1)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
(Box::new(file), Some(fl), uuid_string)
|
2017-10-18 09:20:27 +00:00
|
|
|
} else {
|
2018-04-26 12:15:30 +00:00
|
|
|
// We generate a random uuid for stdout
|
2018-09-30 13:11:36 +00:00
|
|
|
let uuid = Uuid::new_v4().to_hyphenated().to_string();
|
2018-04-26 12:15:30 +00:00
|
|
|
(Box::new(rt.stdout()), None, uuid)
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut input = rt.stdin().unwrap_or_else(|| {
|
|
|
|
error!("No input stream. Cannot ask for permission");
|
|
|
|
exit(1)
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut output = rt.stdout();
|
|
|
|
|
2017-10-18 09:20:27 +00:00
|
|
|
loop {
|
|
|
|
::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template)
|
|
|
|
.map_warn_err_str("Editing failed.")
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap();
|
2017-10-18 09:20:27 +00:00
|
|
|
|
|
|
|
if template == TEMPLATE || template.is_empty() {
|
|
|
|
error!("No (changed) content in tempfile. Not doing anything.");
|
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
2018-02-12 20:05:06 +00:00
|
|
|
match ::toml::de::from_str(&template)
|
2018-11-01 19:33:24 +00:00
|
|
|
.map(|toml| parse_toml_into_vcard(&mut output, &mut input, toml, uuid.clone()))
|
2018-10-30 17:40:53 +00:00
|
|
|
.map_err(Error::from)
|
2018-02-12 20:05:06 +00:00
|
|
|
{
|
2017-10-18 09:20:27 +00:00
|
|
|
Err(e) => {
|
|
|
|
error!("Error parsing template");
|
|
|
|
trace_error(&e);
|
2018-11-01 19:33:24 +00:00
|
|
|
|
|
|
|
if ask_continue(&mut input, &mut output) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
},
|
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Ok(None) => continue,
|
|
|
|
Ok(Some(vcard)) => {
|
|
|
|
if template == TEMPLATE || template.is_empty() {
|
2018-11-01 19:33:24 +00:00
|
|
|
if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false), &mut input, &mut output)
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap()
|
2018-11-01 19:33:24 +00:00
|
|
|
{
|
|
|
|
exit(1)
|
2017-11-08 15:53:58 +00:00
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
let vcard_string = write_component(&vcard);
|
2019-08-27 08:42:46 +00:00
|
|
|
dest
|
2018-02-12 20:05:06 +00:00
|
|
|
.write_all(&vcard_string.as_bytes())
|
2018-10-30 17:40:53 +00:00
|
|
|
.map_err(Error::from)
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap();
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
if let Some(location) = location {
|
|
|
|
if !scmd.is_present("dont-track") {
|
2018-10-06 10:50:12 +00:00
|
|
|
let entry = rt.store()
|
2019-04-16 16:33:02 +00:00
|
|
|
.create_from_path(&location, &ref_config, &collection_name)
|
2019-02-05 00:37:32 +00:00
|
|
|
.map_err_trace_exit_unwrap();
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2019-08-27 08:42:46 +00:00
|
|
|
rt.report_touched(entry.get_location()).unwrap_or_exit();
|
2018-10-06 10:50:12 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
info!("Created entry in store");
|
|
|
|
} else {
|
|
|
|
info!("Not creating entry in store");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
info!("Cannot track stdout-created contact information");
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
info!("Ready");
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2019-08-25 08:37:20 +00:00
|
|
|
fn parse_toml_into_vcard(output: &mut dyn Write, input: &mut dyn Read, toml: Value, uuid: String) -> Option<Vcard> {
|
2018-09-27 12:42:58 +00:00
|
|
|
let mut vcard = VcardBuilder::new().with_uid(uuid);
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
{ // parse name
|
|
|
|
debug!("Parsing name");
|
2018-04-26 12:01:53 +00:00
|
|
|
let firstname = read_str_from_toml(&toml, "name.first", true);
|
2017-11-08 15:53:58 +00:00
|
|
|
trace!("firstname = {:?}", firstname);
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
let lastname = read_str_from_toml(&toml, "name.last", true);
|
2017-11-08 15:53:58 +00:00
|
|
|
trace!("lastname = {:?}", lastname);
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
vcard = vcard.with_name(parameters!(),
|
2018-04-26 12:01:53 +00:00
|
|
|
read_str_from_toml(&toml, "name.prefix", false),
|
2017-11-08 15:53:58 +00:00
|
|
|
firstname.clone(),
|
2018-04-26 12:01:53 +00:00
|
|
|
read_str_from_toml(&toml, "name.additional", false),
|
2017-11-08 15:53:58 +00:00
|
|
|
lastname.clone(),
|
2018-04-26 12:01:53 +00:00
|
|
|
read_str_from_toml(&toml, "name.suffix", false));
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
if let (Some(first), Some(last)) = (firstname, lastname) {
|
|
|
|
trace!("Building fullname: '{} {}'", first, last);
|
|
|
|
vcard = vcard.with_fullname(format!("{} {}", first, last));
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 16:06:31 +00:00
|
|
|
{ // parse personal
|
|
|
|
debug!("Parsing person information");
|
2018-04-26 12:01:53 +00:00
|
|
|
let birthday = read_str_from_toml(&toml, "person.birthday", false);
|
2017-11-08 16:06:31 +00:00
|
|
|
trace!("birthday = {:?}", birthday);
|
|
|
|
|
|
|
|
if let Some(bday) = birthday {
|
|
|
|
vcard = vcard.with_bday(parameters!(), bday);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
{ // parse nicknames
|
|
|
|
debug!("Parsing nicknames");
|
2019-02-05 00:37:32 +00:00
|
|
|
match toml.read("nickname").map_err(Error::from).map_err_trace_exit_unwrap() {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(&Value::Array(ref ary)) => {
|
|
|
|
for (i, element) in ary.iter().enumerate() {
|
2018-04-26 12:01:53 +00:00
|
|
|
let nicktype = match read_str_from_toml(element, "type", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
None => BTreeMap::new(),
|
|
|
|
Some(p) => {
|
|
|
|
let mut m = BTreeMap::new();
|
|
|
|
m.insert("TYPE".into(), p);
|
|
|
|
m
|
2017-10-18 09:20:27 +00:00
|
|
|
},
|
2017-11-08 15:53:58 +00:00
|
|
|
};
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
let name = match read_str_from_toml(element, "name", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(p) => p,
|
|
|
|
None => {
|
|
|
|
error!("Key 'nickname.[{}].name' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
},
|
2017-11-08 15:53:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
trace!("nick type = {:?}", nicktype);
|
|
|
|
trace!("name = {:?}", name);
|
|
|
|
|
|
|
|
vcard = vcard.with_nickname(nicktype, name);
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(&Value::String(ref name)) => {
|
|
|
|
vcard = vcard.with_nickname(parameters!(), name.clone());
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(_) => {
|
|
|
|
error!("Type Error: Expected Array or String at 'nickname'");
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
None => {
|
|
|
|
// nothing
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // parse organisation
|
|
|
|
debug!("Parsing organisation");
|
|
|
|
|
|
|
|
if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") {
|
|
|
|
trace!("orgs = {:?}", orgs);
|
|
|
|
vcard = vcard.with_org(orgs);
|
|
|
|
}
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
if let Some(title) = read_str_from_toml(&toml, "organisation.title", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
trace!("title = {:?}", title);
|
|
|
|
vcard = vcard.with_title(title);
|
|
|
|
}
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
if let Some(role) = read_str_from_toml(&toml, "organisation.role", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
trace!("role = {:?}", role);
|
|
|
|
vcard = vcard.with_role(role);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // parse phone
|
|
|
|
debug!("Parse phone");
|
2019-02-05 00:37:32 +00:00
|
|
|
match toml.read("person.phone").map_err(Error::from).map_err_trace_exit_unwrap() {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(&Value::Array(ref ary)) => {
|
|
|
|
for (i, element) in ary.iter().enumerate() {
|
2018-04-26 12:01:53 +00:00
|
|
|
let phonetype = match read_str_from_toml(element, "type", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(p) => p,
|
2017-10-18 09:20:27 +00:00
|
|
|
None => {
|
2017-11-08 15:53:58 +00:00
|
|
|
error!("Key 'phones.[{}].type' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
let number = match read_str_from_toml(element, "number", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(p) => p,
|
|
|
|
None => {
|
|
|
|
error!("Key 'phones.[{}].number' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
trace!("phonetype = {:?}", phonetype);
|
|
|
|
trace!("number = {:?}", number);
|
|
|
|
|
|
|
|
vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number);
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(_) => {
|
|
|
|
error!("Expected Array at 'phones'.");
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
None => {
|
|
|
|
// nothing
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
{ // parse address
|
|
|
|
debug!("Parsing address");
|
2019-02-05 00:37:32 +00:00
|
|
|
match toml.read("addresses").map_err(Error::from).map_err_trace_exit_unwrap() {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(&Value::Array(ref ary)) => {
|
|
|
|
for (i, element) in ary.iter().enumerate() {
|
2018-04-26 12:01:53 +00:00
|
|
|
let adrtype = match read_str_from_toml(element, "type", false) {
|
2017-10-18 09:20:27 +00:00
|
|
|
None => {
|
2017-11-08 15:53:58 +00:00
|
|
|
error!("Key 'adresses.[{}].type' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
},
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(p) => p,
|
|
|
|
};
|
|
|
|
trace!("adrtype = {:?}", adrtype);
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
let bx = read_str_from_toml(element, "box", false);
|
|
|
|
let extended = read_str_from_toml(element, "extended", false);
|
|
|
|
let street = read_str_from_toml(element, "street", false);
|
|
|
|
let code = read_str_from_toml(element, "code", false);
|
|
|
|
let city = read_str_from_toml(element, "city", false);
|
|
|
|
let region = read_str_from_toml(element, "region", false);
|
|
|
|
let country = read_str_from_toml(element, "country", false);
|
2017-11-08 15:53:58 +00:00
|
|
|
|
|
|
|
trace!("bx = {:?}", bx);
|
|
|
|
trace!("extended = {:?}", extended);
|
|
|
|
trace!("street = {:?}", street);
|
|
|
|
trace!("code = {:?}", code);
|
|
|
|
trace!("city = {:?}", city);
|
|
|
|
trace!("region = {:?}", region);
|
|
|
|
trace!("country = {:?}", country);
|
|
|
|
|
|
|
|
vcard = vcard.with_adr(
|
|
|
|
parameters!("TYPE" => adrtype),
|
|
|
|
bx, extended, street, code, city, region, country
|
|
|
|
);
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(_) => {
|
|
|
|
error!("Type Error: Expected Array at 'addresses'");
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
None => {
|
|
|
|
// nothing
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
{ // parse email
|
|
|
|
debug!("Parsing email");
|
2019-02-05 00:37:32 +00:00
|
|
|
match toml.read("person.email").map_err(Error::from).map_err_trace_exit_unwrap() {
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(&Value::Array(ref ary)) => {
|
|
|
|
for (i, element) in ary.iter().enumerate() {
|
2018-04-26 12:01:53 +00:00
|
|
|
let mailtype = match read_str_from_toml(element, "type", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
None => {
|
|
|
|
error!("Error: 'email.[{}].type' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
Some(p) => p,
|
|
|
|
}; // TODO: Unused, because unsupported by vobject
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
let mail = match read_str_from_toml(element, "addr", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
None => {
|
|
|
|
error!("Error: 'email.[{}].addr' missing", i);
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
Some(p) => p,
|
|
|
|
};
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
trace!("mailtype = {:?} (UNUSED)", mailtype);
|
|
|
|
trace!("mail = {:?}", mail);
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
vcard = vcard.with_email(mail);
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(_) => {
|
|
|
|
error!("Type Error: Expected Array at 'email'");
|
2018-11-01 19:33:24 +00:00
|
|
|
if ask_continue(input, output) {
|
|
|
|
return None
|
|
|
|
} else {
|
|
|
|
exit(1)
|
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
},
|
|
|
|
None => {
|
|
|
|
// nothing
|
|
|
|
},
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 15:53:58 +00:00
|
|
|
{ // parse others
|
|
|
|
debug!("Parsing others");
|
2017-11-08 16:41:59 +00:00
|
|
|
if let Some(categories) = read_strary_from_toml(&toml, "other.categories") {
|
2017-11-08 15:53:58 +00:00
|
|
|
vcard = vcard.with_categories(categories);
|
|
|
|
} else {
|
|
|
|
debug!("No categories");
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
if let Some(webpage) = read_str_from_toml(&toml, "other.webpage", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
vcard = vcard.with_url(webpage);
|
|
|
|
} else {
|
|
|
|
debug!("No webpage");
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
if let Some(note) = read_str_from_toml(&toml, "other.note", false) {
|
2017-11-08 15:53:58 +00:00
|
|
|
vcard = vcard.with_note(note);
|
2017-10-18 09:20:27 +00:00
|
|
|
} else {
|
2017-11-08 15:53:58 +00:00
|
|
|
debug!("No note");
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
2017-11-08 15:53:58 +00:00
|
|
|
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
|
2018-09-27 12:42:58 +00:00
|
|
|
let vcard = vcard
|
|
|
|
.build()
|
|
|
|
.unwrap(); // TODO: This unwrap does not fail with rust-vobject, why is there a Result<> returned?
|
2017-11-08 15:53:58 +00:00
|
|
|
Some(vcard)
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
|
2018-10-30 17:40:53 +00:00
|
|
|
match toml.read(path).map_err(Error::from).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
|
2017-10-18 09:20:27 +00:00
|
|
|
Ok(Some(&Value::Array(ref vec))) => {
|
|
|
|
let mut v = Vec::new();
|
|
|
|
for elem in vec {
|
|
|
|
match *elem {
|
|
|
|
Value::String(ref s) => v.push(s.clone()),
|
|
|
|
_ => {
|
|
|
|
error!("Type Error: '{}' must be Array<String>", path);
|
|
|
|
return None
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(v)
|
|
|
|
}
|
|
|
|
Ok(Some(&Value::String(ref s))) => {
|
|
|
|
warn!("Having String, wanting Array<String> ... going to auto-fix");
|
|
|
|
Some(vec![s.clone()])
|
|
|
|
},
|
|
|
|
Ok(Some(_)) => {
|
|
|
|
error!("Type Error: '{}' must be Array<String>", path);
|
|
|
|
None
|
|
|
|
},
|
|
|
|
Ok(None) => None,
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-26 12:01:53 +00:00
|
|
|
fn read_str_from_toml(toml: &Value, path: &'static str, must_be_there: bool) -> Option<String> {
|
2017-10-18 09:20:27 +00:00
|
|
|
let v = toml.read(path)
|
2018-10-30 17:40:53 +00:00
|
|
|
.map_err(Error::from)
|
2017-10-18 09:20:27 +00:00
|
|
|
.map_warn_err_str(&format!("Failed to read value at '{}'", path));
|
|
|
|
|
|
|
|
match v {
|
|
|
|
Ok(Some(&Value::String(ref s))) => Some(s.clone()),
|
|
|
|
Ok(Some(_)) => {
|
|
|
|
error!("Type Error: '{}' must be String", path);
|
|
|
|
None
|
|
|
|
},
|
|
|
|
Ok(None) => {
|
2018-04-26 12:01:53 +00:00
|
|
|
if must_be_there {
|
|
|
|
error!("Expected '{}' to be present, but is not.", path);
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
None
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
trace_error(&e);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2017-11-08 16:15:52 +00:00
|
|
|
mod test_parsing {
|
|
|
|
use super::parse_toml_into_vcard;
|
2018-11-01 19:33:24 +00:00
|
|
|
use std::io::empty;
|
2017-11-08 16:15:52 +00:00
|
|
|
|
2017-10-18 09:20:27 +00:00
|
|
|
// TODO
|
2019-08-27 08:42:46 +00:00
|
|
|
const TEMPLATE : &str = include_str!("../static/new-contact-template-test.toml");
|
2017-11-08 16:15:52 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_names() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert!(vcard.name().is_some());
|
|
|
|
|
2018-04-26 12:15:30 +00:00
|
|
|
assert_eq!(vcard.uid().unwrap().raw(), "uid");
|
2017-11-08 16:15:52 +00:00
|
|
|
assert_eq!(vcard.name().unwrap().surname().unwrap(), "test");
|
|
|
|
assert_eq!(vcard.name().unwrap().given_name().unwrap(), "test");
|
|
|
|
assert_eq!(vcard.name().unwrap().additional_names().unwrap(), "test");
|
|
|
|
assert_eq!(vcard.name().unwrap().honorific_prefixes().unwrap(), "test");
|
|
|
|
assert_eq!(vcard.name().unwrap().honorific_suffixes().unwrap(), "test");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_person() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert!(vcard.bday().is_some());
|
|
|
|
|
|
|
|
assert_eq!(vcard.bday().unwrap().raw(), "2017-01-01");
|
|
|
|
|
|
|
|
assert_eq!(vcard.nickname().len(), 1);
|
|
|
|
assert_eq!(vcard.nickname()[0].raw(), "boss");
|
|
|
|
|
|
|
|
// TODO: parameters() not yet implemented in underlying API
|
|
|
|
// assert!(vcard.nickname()[0].parameters().contains_key("work"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_organization() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(vcard.org().len(), 1);
|
|
|
|
assert_eq!(vcard.org()[0].raw(), "test");
|
|
|
|
|
|
|
|
assert_eq!(vcard.title().len(), 1);
|
|
|
|
assert_eq!(vcard.title()[0].raw(), "test");
|
|
|
|
|
|
|
|
assert_eq!(vcard.role().len(), 1);
|
|
|
|
assert_eq!(vcard.role()[0].raw(), "test");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_phone() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(vcard.tel().len(), 1);
|
|
|
|
assert_eq!(vcard.tel()[0].raw(), "0123 123456789");
|
|
|
|
|
|
|
|
// TODO: parameters() not yet implemented in underlying API
|
|
|
|
// assert!(vcard.tel()[0].parameters().contains_key("type"));
|
|
|
|
// assert_eq!(vcard.tel()[0].parameters().get("type").unwrap(), "home");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_email() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(vcard.email().len(), 1);
|
|
|
|
assert_eq!(vcard.email()[0].raw(), "examle@examplemail.org");
|
|
|
|
|
|
|
|
// TODO: parameters() not yet implemented in underlying API
|
|
|
|
// assert!(vcard.email()[0].parameters().contains_key("type"));
|
|
|
|
// assert_eq!(vcard.email()[0].parameters().get("type").unwrap(), "home");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_addresses() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(vcard.adr().len(), 1);
|
|
|
|
assert_eq!(vcard.adr()[0].raw(), "testbox;testextended;teststreet;testcode;testcity;testregion;testcountry");
|
|
|
|
|
|
|
|
// TODO: parameters() not yet implemented in underlying API
|
|
|
|
//for e in &["box", "extended", "street", "code", "city", "region", "country"] {
|
|
|
|
// assert!(vcard.adr()[0].parameters().contains_key(e));
|
|
|
|
// assert_eq!(vcard.adr()[0].parameters().get(e).unwrap(), "test");
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_template_other() {
|
2018-04-26 12:15:30 +00:00
|
|
|
let uid = String::from("uid");
|
2018-11-01 19:33:24 +00:00
|
|
|
let mut output = Vec::new();
|
|
|
|
let vcard = parse_toml_into_vcard(&mut output, &mut empty(), ::toml::de::from_str(TEMPLATE).unwrap(), uid);
|
2017-11-08 16:15:52 +00:00
|
|
|
assert!(vcard.is_some(), "Failed to parse test template.");
|
|
|
|
let vcard = vcard.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(vcard.categories().len(), 1);
|
|
|
|
assert_eq!(vcard.categories()[0].raw(), "test");
|
|
|
|
|
|
|
|
assert_eq!(vcard.url().len(), 1);
|
|
|
|
assert_eq!(vcard.url()[0].raw(), "test");
|
|
|
|
|
|
|
|
assert_eq!(vcard.note().len(), 1);
|
|
|
|
assert_eq!(vcard.note()[0].raw(), "test");
|
|
|
|
}
|
2017-10-18 09:20:27 +00:00
|
|
|
}
|
|
|
|
|