diff --git a/Cargo.toml b/Cargo.toml index 8dba3147..1f2b6dee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ ".imag-documentation", "bin/core/imag", + "bin/core/imag-gps", "bin/core/imag-grep", "bin/core/imag-link", "bin/core/imag-ref", diff --git a/bin/core/imag-gps/Cargo.toml b/bin/core/imag-gps/Cargo.toml new file mode 100644 index 00000000..d72e9902 --- /dev/null +++ b/bin/core/imag-gps/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "imag-gps" +version = "0.4.0" +authors = ["Matthias Beyer "] + +description = "Part of the imag core distribution: imag-gps command" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../../../README.md" +license = "LGPL-2.1" + +documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[dependencies] +clap = ">=2.17" +log = "0.3" +version = "2.0.1" +url = "1.2" +toml = "0.4" +toml-query = "0.3.0" + +libimagstore = { version = "0.4.0", path = "../../../lib/core/libimagstore" } +libimagrt = { version = "0.4.0", path = "../../../lib/core/libimagrt" } +libimagerror = { version = "0.4.0", path = "../../../lib/core/libimagerror" } +libimagentrygps = { version = "0.4.0", path = "../../../lib/entry/libimagentrygps" } +libimagutil = { version = "0.4.0", path = "../../../lib/etc/libimagutil" } + +[dev-dependencies.libimagutil] +version = "0.4.0" +path = "../../../lib/etc/libimagutil" +default-features = false +features = ["testing"] + diff --git a/bin/core/imag-gps/src/main.rs b/bin/core/imag-gps/src/main.rs new file mode 100644 index 00000000..56da4efe --- /dev/null +++ b/bin/core/imag-gps/src/main.rs @@ -0,0 +1,183 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer 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 +// + +#![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, +)] + +extern crate clap; +#[macro_use] +extern crate log; +#[macro_use] +extern crate version; + +extern crate libimagentrygps; +extern crate libimagrt; +extern crate libimagutil; +extern crate libimagerror; +extern crate libimagstore; + +use std::process::exit; +use std::path::PathBuf; +use std::str::FromStr; + +use libimagentrygps::types::*; +use libimagentrygps::entry::*; +use libimagrt::setup::generate_runtime_setup; +use libimagrt::runtime::Runtime; +use libimagutil::warn_exit::warn_exit; +use libimagerror::trace::MapErrTrace; +use libimagstore::storeid::IntoStoreId; + +mod ui; + +fn main() { + let rt = generate_runtime_setup("imag-gps", + &version!()[..], + "Add GPS coordinates to entries", + ui::build_ui); + + rt.cli() + .subcommand_name() + .map(|name| { + match name { + "add" => add(&rt), + "remove" => remove(&rt), + "get" => get(&rt), + _ => warn_exit("No commandline call", 1) + } + }); +} + +fn add(rt: &Runtime) { + let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main() + + let entry_name = scmd.value_of("entry").unwrap(); // safed by clap + let sid = PathBuf::from(entry_name) + .into_storeid() + .map_err_trace_exit(1) + .unwrap(); // safed by above call + + let c = { + let parse = |value: &str| -> Vec { + value.split(".") + .map(FromStr::from_str) + .map(|elem| elem.map_err_trace_exit(1).unwrap()) + .collect::>() + }; + + let long = parse(scmd.value_of("longitude").unwrap()); // unwrap safed by clap + let lati = parse(scmd.value_of("latitude").unwrap()); // unwrap safed by clap + + let long = GPSValue::new(long[0], long[1], long[2]); + let lati = GPSValue::new(lati[0], lati[1], lati[2]); + + Coordinates::new(long, lati) + }; + + rt.store() + .get(sid) + .map_err_trace_exit(1) + .unwrap() // safed by above call + .map(|mut entry| { + let _ = entry.set_coordinates(c) + .map_err_trace_exit(1); + }) + .unwrap_or_else(|| { + error!("No such entry: {}", entry_name); + exit(1) + }); +} + +fn remove(rt: &Runtime) { + let scmd = rt.cli().subcommand_matches("remove").unwrap(); // safed by main() + + let entry_name = scmd.value_of("entry").unwrap(); // safed by clap + let sid = PathBuf::from(entry_name) + .into_storeid() + .map_err_trace_exit(1) + .unwrap(); // safed by above call + + let removed_value = rt + .store() + .get(sid) + .map_err_trace_exit(1) + .unwrap() // safed by above call + .unwrap_or_else(|| { // if we have Ok(None) + error!("No such entry: {}", entry_name); + exit(1) + }) + .remove_coordinates() + .map_err_trace_exit(1) // The delete action failed + .unwrap() // safed by above call + .unwrap_or_else(|| { // if we have Ok(None) + error!("Entry had no coordinates: {}", entry_name); + exit(1) + }) + .map_err_trace_exit(1) // The parsing of the deleted values failed + .unwrap(); // safed by above call + + if scmd.is_present("print-removed") { + println!("{}", removed_value); + } + + info!("Ok"); +} + +fn get(rt: &Runtime) { + let scmd = rt.cli().subcommand_matches("get").unwrap(); // safed by main() + + let entry_name = scmd.value_of("entry").unwrap(); // safed by clap + let sid = PathBuf::from(entry_name) + .into_storeid() + .map_err_trace_exit(1) + .unwrap(); // safed by above call + + let value = rt + .store() + .get(sid) + .map_err_trace_exit(1) + .unwrap() // safed by above call + .unwrap_or_else(|| { // if we have Ok(None) + error!("No such entry: {}", entry_name); + exit(1) + }) + .get_coordinates() + .map_err_trace_exit(1) // The get action failed + .unwrap() // safed by above call + .unwrap_or_else(|| { // if we have Ok(None) + error!("Entry has no coordinates: {}", entry_name); + exit(1) + }); + + println!("{}", value); + info!("Ok"); +} + diff --git a/bin/core/imag-gps/src/ui.rs b/bin/core/imag-gps/src/ui.rs new file mode 100644 index 00000000..b96dc068 --- /dev/null +++ b/bin/core/imag-gps/src/ui.rs @@ -0,0 +1,91 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer 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 clap::{Arg, App, SubCommand}; + +pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app + .subcommand(SubCommand::with_name("add") + .about("Add GPS coordinates to an entry") + .version("0.1") + .arg(Arg::with_name("longitude") + .long("long") + .takes_value(true) + .required(true) + .multiple(false) + .help("Set the longitude value. Format: ..") + .value_name("LONGITUDE")) + .arg(Arg::with_name("latitude") + .long("lat") + .takes_value(true) + .required(true) + .multiple(false) + .help("Set the latitude. Format: ..") + .value_name("LATITUDE")) + .arg(Arg::with_name("entry") + .index(1) + .takes_value(true) + .required(true) + .multiple(false) + .help("The entry to add the latitude/longitude to") + .value_name("ENTRY")) + ) + + .subcommand(SubCommand::with_name("remove") + .about("Remove a GPS coordinate pair from an entry") + .version("0.1") + .arg(Arg::with_name("print-removed") + .long("print-removed") + .short("p") + .takes_value(false) + .required(false) + .help("Print the removed values after removing them")) + .arg(Arg::with_name("entry") + .index(1) + .takes_value(true) + .required(true) + .multiple(false) + .help("The entry to remove the latitude/longitude from") + .value_name("ENTRY")) + ) + + .subcommand(SubCommand::with_name("get") + .about("Get a GPS coordinate pair from an entry") + .version("0.1") + .arg(Arg::with_name("entry") + .index(1) + .takes_value(true) + .required(true) + .multiple(false) + .help("The entry to get the latitude/longitude from") + .value_name("ENTRY")) + .arg(Arg::with_name("format-json") + .long("json") + .takes_value(false) + .required(false) + .multiple(false) + .help("Get as JSON Object")) + .arg(Arg::with_name("format-print") + .long("print") + .takes_value(false) + .required(false) + .multiple(false) + .help("Print as = pairs (2 lines, default)")) + ) +} diff --git a/doc/src/04020-module-gps.md b/doc/src/04020-module-gps.md index 2a2a8353..28e444e8 100644 --- a/doc/src/04020-module-gps.md +++ b/doc/src/04020-module-gps.md @@ -2,5 +2,3 @@ The GPS module is a plumbing command for attaching a GPS coordinate to an entry. - - diff --git a/lib/entry/libimagentrygps/src/entry.rs b/lib/entry/libimagentrygps/src/entry.rs index 13b73ef5..1e54157e 100644 --- a/lib/entry/libimagentrygps/src/entry.rs +++ b/lib/entry/libimagentrygps/src/entry.rs @@ -72,10 +72,31 @@ impl GPSEntry for Entry { } fn remove_coordinates(&mut self) -> Result>> { - self.get_header_mut() - .delete("gps.coordinates") - .chain_err(|| GPSEK::HeaderWriteError) - .map(|opt| opt.as_ref().map(Coordinates::from_value)) + let coordinates = self.get_coordinates(); + + let patterns = [ + "gps.coordinates.latitude.degree", + "gps.coordinates.latitude.minutes", + "gps.coordinates.latitude.seconds", + "gps.coordinates.longitude.degree", + "gps.coordinates.longitude.minutes", + "gps.coordinates.longitude.seconds", + "gps.coordinates.latitude", + "gps.coordinates.longitude", + "gps.coordinates", + "gps", + ]; + + let mut hdr = self.get_header_mut(); + for pattern in patterns.iter() { + let _ = try!(hdr.delete(pattern).chain_err(|| GPSEK::HeaderWriteError)); + } + + match coordinates { + Ok(None) => Ok(None), + Ok(Some(some)) => Ok(Some(Ok(some))), + Err(e) => Ok(Some(Err(e))), + } } }