Merge pull request #1338 from matthiasbeyer/libimagcontact/to-json-output
libimagcontact/imag-contact: json output support
This commit is contained in:
commit
5609651f11
6 changed files with 271 additions and 28 deletions
|
@ -29,16 +29,22 @@ handlebars = "0.29"
|
||||||
vobject = "0.4"
|
vobject = "0.4"
|
||||||
walkdir = "1"
|
walkdir = "1"
|
||||||
uuid = { version = "0.6", features = ["v4"] }
|
uuid = { version = "0.6", features = ["v4"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
libimagrt = { version = "0.7.0", path = "../../../lib/core/libimagrt" }
|
libimagrt = { version = "0.7.0", path = "../../../lib/core/libimagrt" }
|
||||||
libimagstore = { version = "0.7.0", path = "../../../lib/core/libimagstore" }
|
libimagstore = { version = "0.7.0", path = "../../../lib/core/libimagstore" }
|
||||||
libimagerror = { version = "0.7.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.7.0", path = "../../../lib/core/libimagerror" }
|
||||||
libimagcontact = { version = "0.7.0", path = "../../../lib/domain/libimagcontact" }
|
|
||||||
libimagutil = { version = "0.7.0", path = "../../../lib/etc/libimagutil" }
|
libimagutil = { version = "0.7.0", path = "../../../lib/etc/libimagutil" }
|
||||||
libimagentryref = { version = "0.7.0", path = "../../../lib/entry/libimagentryref" }
|
libimagentryref = { version = "0.7.0", path = "../../../lib/entry/libimagentryref" }
|
||||||
libimagentryedit = { version = "0.7.0", path = "../../../lib/entry/libimagentryedit" }
|
libimagentryedit = { version = "0.7.0", path = "../../../lib/entry/libimagentryedit" }
|
||||||
libimaginteraction = { version = "0.7.0", path = "../../../lib/etc/libimaginteraction" }
|
libimaginteraction = { version = "0.7.0", path = "../../../lib/etc/libimaginteraction" }
|
||||||
|
|
||||||
|
[dependencies.libimagcontact]
|
||||||
|
version = "0.7.0"
|
||||||
|
path = "../../../lib/domain/libimagcontact"
|
||||||
|
default-features = false
|
||||||
|
features = ["deser"]
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = ">=2.29"
|
version = ">=2.29"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
@ -40,6 +40,7 @@ extern crate toml_query;
|
||||||
extern crate handlebars;
|
extern crate handlebars;
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
extern crate libimagcontact;
|
extern crate libimagcontact;
|
||||||
extern crate libimagstore;
|
extern crate libimagstore;
|
||||||
|
@ -70,6 +71,7 @@ use libimagcontact::store::ContactStore;
|
||||||
use libimagcontact::store::UniqueContactPathGenerator;
|
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 libimagstore::iter::get::StoreIdGetIteratorExtension;
|
use libimagstore::iter::get::StoreIdGetIteratorExtension;
|
||||||
use libimagentryref::reference::Ref;
|
use libimagentryref::reference::Ref;
|
||||||
use libimagentryref::refstore::RefStore;
|
use libimagentryref::refstore::RefStore;
|
||||||
|
@ -111,7 +113,7 @@ fn list(rt: &Runtime) {
|
||||||
let scmd = rt.cli().subcommand_matches("list").unwrap();
|
let scmd = rt.cli().subcommand_matches("list").unwrap();
|
||||||
let list_format = get_contact_print_format("contact.list_format", rt, &scmd);
|
let list_format = get_contact_print_format("contact.list_format", rt, &scmd);
|
||||||
|
|
||||||
let _ = rt
|
let iterator = rt
|
||||||
.store()
|
.store()
|
||||||
.all_contacts()
|
.all_contacts()
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
@ -126,27 +128,46 @@ fn list(rt: &Runtime) {
|
||||||
.get_contact_data()
|
.get_contact_data()
|
||||||
.map(|cd| (fle, cd))
|
.map(|cd| (fle, cd))
|
||||||
.map(|(fle, cd)| (fle, cd.into_inner()))
|
.map(|(fle, cd)| (fle, cd.into_inner()))
|
||||||
.map(|(fle, cd)| (fle, Vcard::from_component(cd)))
|
.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)
|
.map_err_trace_exit_unwrap(1)
|
||||||
})
|
})
|
||||||
.enumerate()
|
.enumerate();
|
||||||
.map(|(i, (fle, vcard))| {
|
|
||||||
let hash = String::from(fle.get_hash().map_err_trace_exit_unwrap(1));
|
if scmd.is_present("json") {
|
||||||
let vcard = vcard.unwrap_or_else(|e| {
|
let v : Vec<DeserVcard> = iterator
|
||||||
error!("Element is not a VCARD object: {:?}", e);
|
.map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect();
|
||||||
exit(1)
|
match ::serde_json::to_string(&v) {
|
||||||
|
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error generating JSON: {:?}", e);
|
||||||
|
::std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
list_format.render("format", &data)
|
||||||
|
.err_from_str()
|
||||||
|
.map_err(CE::from)
|
||||||
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// collect, so that we can have rendered all the things and printing is faster.
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|s| {
|
||||||
|
writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit()
|
||||||
});
|
});
|
||||||
|
}
|
||||||
let data = build_data_object_for_handlebars(i, hash, &vcard);
|
|
||||||
|
|
||||||
let s = list_format.render("format", &data)
|
|
||||||
.err_from_str()
|
|
||||||
.map_err(CE::from)
|
|
||||||
.map_err_trace_exit_unwrap(1);
|
|
||||||
|
|
||||||
writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import(rt: &Runtime) {
|
fn import(rt: &Runtime) {
|
||||||
|
@ -227,7 +248,8 @@ fn find(rt: &Runtime) {
|
||||||
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 list_format = get_contact_print_format("contact.list_format", rt, &scmd);
|
let list_format = get_contact_print_format("contact.list_format", rt, &scmd);
|
||||||
|
|
||||||
rt.store()
|
let iterator = rt
|
||||||
|
.store()
|
||||||
.all_contacts()
|
.all_contacts()
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
.into_get_iter(rt.store())
|
.into_get_iter(rt.store())
|
||||||
|
@ -267,7 +289,20 @@ fn find(rt: &Runtime) {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.enumerate()
|
.enumerate();
|
||||||
|
|
||||||
|
if scmd.is_present("json") {
|
||||||
|
let v : Vec<DeserVcard> = iterator
|
||||||
|
.map(|(_, (_, vcard))| DeserVcard::from(vcard)).collect();
|
||||||
|
match ::serde_json::to_string(&v) {
|
||||||
|
Ok(s) => writeln!(rt.stdout(), "{}", s).to_exit_code().unwrap_or_exit(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error generating JSON: {:?}", e);
|
||||||
|
::std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iterator
|
||||||
.for_each(|(i, (fle, card))| {
|
.for_each(|(i, (fle, card))| {
|
||||||
let fmt = if scmd.is_present("find-show") {
|
let fmt = if scmd.is_present("find-show") {
|
||||||
&show_format
|
&show_format
|
||||||
|
@ -289,6 +324,7 @@ fn find(rt: &Runtime) {
|
||||||
.to_exit_code()
|
.to_exit_code()
|
||||||
.unwrap_or_exit();
|
.unwrap_or_exit();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_contact_print_format(config_value_path: &'static str, rt: &Runtime, scmd: &ArgMatches) -> Handlebars {
|
fn get_contact_print_format(config_value_path: &'static str, rt: &Runtime, scmd: &ArgMatches) -> Handlebars {
|
||||||
|
|
|
@ -38,6 +38,12 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
.multiple(false)
|
.multiple(false)
|
||||||
.value_name("FORMAT")
|
.value_name("FORMAT")
|
||||||
.help("Format to format the listing"))
|
.help("Format to format the listing"))
|
||||||
|
.arg(Arg::with_name("json")
|
||||||
|
.long("json")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.multiple(false)
|
||||||
|
.help("Print output as JSON"))
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("import")
|
.subcommand(SubCommand::with_name("import")
|
||||||
|
@ -102,6 +108,15 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
.conflicts_with("find-show")
|
.conflicts_with("find-show")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.arg(Arg::with_name("json")
|
||||||
|
.long("json")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.multiple(false)
|
||||||
|
.help("Print output as JSON")
|
||||||
|
.conflicts_with("find-show")
|
||||||
|
.conflicts_with("find-list"))
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("create")
|
.subcommand(SubCommand::with_name("create")
|
||||||
|
|
|
@ -20,12 +20,14 @@ is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" }
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = "0.11"
|
error-chain = "0.11"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
toml-query = "0.4"
|
toml-query = "0.4"
|
||||||
vobject = "0.4"
|
vobject = "0.4"
|
||||||
uuid = { version = "0.6", features = ["v4"] }
|
uuid = { version = "0.6", features = ["v4"] }
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
|
serde_derive = { version = "1", optional = true }
|
||||||
|
|
||||||
libimagstore = { version = "0.7.0", path = "../../../lib/core/libimagstore" }
|
libimagstore = { version = "0.7.0", path = "../../../lib/core/libimagstore" }
|
||||||
libimagerror = { version = "0.7.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.7.0", path = "../../../lib/core/libimagerror" }
|
||||||
|
@ -37,3 +39,7 @@ path = "../../../lib/entry/libimagentryref/"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["generators", "generators-sha1"]
|
features = ["generators", "generators-sha1"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
deser = ["serde", "serde_derive"]
|
||||||
|
|
||||||
|
|
170
lib/domain/libimagcontact/src/deser.rs
Normal file
170
lib/domain/libimagcontact/src/deser.rs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; version
|
||||||
|
// 2.1 of the License.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
//
|
||||||
|
|
||||||
|
use 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)]
|
||||||
|
pub struct DeserVcard {
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
adr : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
anniversary : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
bday : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
categories : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
clientpidmap : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
email : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
fullname : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
gender : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
geo : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
impp : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
key : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
lang : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
logo : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
member : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
name : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
nickname : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
note : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
org : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
photo : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
proid : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
related : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
rev : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
role : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
sound : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
tel : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
title : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
tz : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
uid : Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
url : Vec<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
version : Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vcard> for DeserVcard {
|
||||||
|
fn from(card: Vcard) -> DeserVcard {
|
||||||
|
macro_rules! arystr {
|
||||||
|
($v:expr) => {
|
||||||
|
$v.into_iter().map(|o| o.raw().clone()).collect()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
macro_rules! optstr {
|
||||||
|
($o:expr) => {
|
||||||
|
$o.map(|o| o.raw().clone())
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
DeserVcard {
|
||||||
|
adr : arystr!(card.adr()),
|
||||||
|
anniversary : optstr!(card.anniversary()),
|
||||||
|
bday : optstr!(card.bday()),
|
||||||
|
categories : arystr!(card.categories()),
|
||||||
|
clientpidmap : optstr!(card.clientpidmap()),
|
||||||
|
email : arystr!(card.email()),
|
||||||
|
fullname : arystr!(card.fullname()),
|
||||||
|
gender : optstr!(card.gender()),
|
||||||
|
geo : arystr!(card.geo()),
|
||||||
|
impp : arystr!(card.impp()),
|
||||||
|
key : arystr!(card.key()),
|
||||||
|
lang : arystr!(card.lang()),
|
||||||
|
logo : arystr!(card.logo()),
|
||||||
|
member : arystr!(card.member()),
|
||||||
|
name : optstr!(card.name()),
|
||||||
|
nickname : arystr!(card.nickname()),
|
||||||
|
note : arystr!(card.note()),
|
||||||
|
org : arystr!(card.org()),
|
||||||
|
photo : arystr!(card.photo()),
|
||||||
|
proid : optstr!(card.proid()),
|
||||||
|
related : arystr!(card.related()),
|
||||||
|
rev : optstr!(card.rev()),
|
||||||
|
role : arystr!(card.role()),
|
||||||
|
sound : arystr!(card.sound()),
|
||||||
|
tel : arystr!(card.tel()),
|
||||||
|
title : arystr!(card.title()),
|
||||||
|
tz : arystr!(card.tz()),
|
||||||
|
uid : optstr!(card.uid()),
|
||||||
|
url : arystr!(card.url()),
|
||||||
|
version : optstr!(card.version()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,3 +53,13 @@ pub mod iter;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue