From 2e6df0e458a52ade3189f2613376ab50cfbd9c5e Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Sep 2019 14:08:59 +0200 Subject: [PATCH] Add imag-calendar This patch adds a imag-calendar command which features only a "import" command by now. This can be used to import calendar entries (events) to imag. Signed-off-by: Matthias Beyer --- Cargo.toml | 1 + bin/domain/imag-calendar/Cargo.toml | 46 ++++++++ bin/domain/imag-calendar/src/main.rs | 150 +++++++++++++++++++++++++++ bin/domain/imag-calendar/src/ui.rs | 71 +++++++++++++ scripts/release.sh | 1 + 5 files changed, 269 insertions(+) create mode 100644 bin/domain/imag-calendar/Cargo.toml create mode 100644 bin/domain/imag-calendar/src/main.rs create mode 100644 bin/domain/imag-calendar/src/ui.rs diff --git a/Cargo.toml b/Cargo.toml index 97a0d53b..1e52bdaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "bin/core/imag-tag", "bin/core/imag-view", "bin/domain/imag-bookmark", + "bin/domain/imag-calendar", "bin/domain/imag-contact", "bin/domain/imag-diary", "bin/domain/imag-habit", diff --git a/bin/domain/imag-calendar/Cargo.toml b/bin/domain/imag-calendar/Cargo.toml new file mode 100644 index 00000000..6e320c9a --- /dev/null +++ b/bin/domain/imag-calendar/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "imag-calendar" +version = "0.10.0" +authors = ["Matthias Beyer "] +edition = "2018" + +description = "Part of the imag core distribution: imag-calendar command" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../../../README.md" +license = "LGPL-2.1" + +documentation = "https://imag-pim.org/doc/" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[badges] +travis-ci = { repository = "matthiasbeyer/imag" } +is-it-maintained-issue-resolution = { repository = "matthiasbeyer/imag" } +is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" } +maintenance = { status = "actively-developed" } + +[dependencies] +log = "0.4" +failure = "0.1" +walkdir = "2.2.8" + +libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } +libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } +libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } +libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" } +libimagentryref = { version = "0.10.0", path = "../../../lib/entry/libimagentryref" } +libimagentryedit = { version = "0.10.0", path = "../../../lib/entry/libimagentryedit" } +libimaginteraction = { version = "0.10.0", path = "../../../lib/etc/libimaginteraction" } +libimagcalendar = { version = "0.10.0", path = "../../../lib/domain/libimagcalendar" } + +[dependencies.clap] +version = "2.33.0" +default-features = false +features = ["color", "suggestions", "wrap_help"] + +[dependencies.toml-query] +version = "0.9.2" +default-features = false +features = ["typed"] + diff --git a/bin/domain/imag-calendar/src/main.rs b/bin/domain/imag-calendar/src/main.rs new file mode 100644 index 00000000..624a2c63 --- /dev/null +++ b/bin/domain/imag-calendar/src/main.rs @@ -0,0 +1,150 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 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 +// + +#![forbid(unsafe_code)] + +#![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, +)] + +#[macro_use] extern crate failure; +#[macro_use] extern crate log; +extern crate clap; +extern crate toml_query; +extern crate walkdir; + +#[macro_use] extern crate libimagrt; +extern crate libimagcalendar; +extern crate libimagerror; +extern crate libimagstore; +extern crate libimagutil; + +use std::path::PathBuf; + +use failure::Error; +use failure::Fallible as Result; +use toml_query::read::Partial; +use toml_query::read::TomlValueReadExt; +use walkdir::DirEntry; +use walkdir::WalkDir; + +use libimagcalendar::store::EventStore; +use libimagerror::exit::ExitUnwrap; +use libimagerror::iter::TraceIterator; +use libimagerror::trace::MapErrTrace; +use libimagrt::runtime::Runtime; +use libimagrt::setup::generate_runtime_setup; + +mod ui; + +fn main() { + let version = make_imag_version!(); + let rt = generate_runtime_setup("imag-calendar", + &version, + "Calendar management tool", + crate::ui::build_ui); + + + if let Some(name) = rt.cli().subcommand_name() { + debug!("Call {}", name); + match name { + "import" => import(&rt), + other => { + warn!("Right now, only the 'import' command is available"); + debug!("Unknown command"); + let _ = rt.handle_unknown_subcommand("imag-calendar", other, rt.cli()) + .map_err_trace_exit_unwrap() + .code() + .map(::std::process::exit); + }, + } + } +} + +fn import(rt: &Runtime) { + let scmd = rt.cli().subcommand_matches("import").unwrap(); // safe by clap + let collection_name = rt.cli().value_of("calendar-ref-collection-name").unwrap(); // default by clap + let do_fail = scmd.is_present("import-fail"); + let force_override = scmd.is_present("import-force-override"); + 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(); + + scmd.values_of("filesordirs") + .unwrap() // save by clap + .into_iter() + .map(PathBuf::from) + .map(|path| if path.is_dir() { // Find all files + Box::new(WalkDir::new(path) + .follow_links(false) + .into_iter() + .filter_entry(is_not_hidden) + .filter_map(|r| match r { + Err(e) => Some(Err(Error::from(e))), + Ok(fe) => { + if fe.file_type().is_file() { + Some(Ok(fe.into_path())) + } else { + None // filter out directories + } + } + })) as Box>> + } else { // is file, ensured by clap validator + Box::new(std::iter::once(Ok(path))) + }) + .flat_map(|it| it) // From Iter>> to Iter> + .trace_unwrap_exit() //... to Iter + .map(|path| { + let v = rt.store().import_from_path(path, collection_name, &ref_config, force_override)?; + Ok(v.into_iter() + .filter_map(|result| if do_fail { + Some(result.map_err_trace_exit_unwrap()) + } else { + match result { + Err(e) => { warn!("Error while importing: {}", e); None } + Ok(fle) => Some(fle), + } + })) + }) + .trace_unwrap_exit() + .flat_map(|it| it) + .for_each(|fle| rt.report_touched(fle.get_location()).unwrap_or_exit()); +} + +/// helper function to check whether a DirEntry points to something hidden (starting with dot) +fn is_not_hidden(entry: &DirEntry) -> bool { + !entry.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false) +} + diff --git a/bin/domain/imag-calendar/src/ui.rs b/bin/domain/imag-calendar/src/ui.rs new file mode 100644 index 00000000..3a8f94c7 --- /dev/null +++ b/bin/domain/imag-calendar/src/ui.rs @@ -0,0 +1,71 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 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 + .arg(Arg::with_name("calendar-ref-collection-name") + .long("ref-collection") + .takes_value(true) + .required(false) + .multiple(false) + .default_value("calendars") + .help("Name (Key) of the basepath setting in the configuration file to use")) + + .subcommand(SubCommand::with_name("import") + .about("Import directory of calendar files or files directl") + .version("0.1") + .arg(Arg::with_name("filesordirs") + .index(1) + .takes_value(true) + .required(true) + .multiple(true) + .value_name("PATH") + .validator(import_validator) + .help("Import files from this directory (or specify files directly)")) + + .arg(Arg::with_name("import-fail") + .short("F") + .long("fail") + .takes_value(false) + .required(false) + .multiple(false) + .help("Fail if a file cannot be parsed (if directory is given, all files found must be icalendar files)")) + + .arg(Arg::with_name("import-force-override") + .long("force") + .takes_value(false) + .required(false) + .multiple(false) + .help("Override if entry for event already exists")) + ) +} + +fn import_validator>(s: A) -> Result<(), String> { + use libimagutil::cli_validators::*; + + is_existing_path(s.as_ref())?; + + match (is_file(s.as_ref()), is_directory(s.as_ref())) { + (Err(_), Err(_)) => Err(format!("Not a file or directory: {}", s.as_ref())), + _ => Ok(()) + } +} + diff --git a/scripts/release.sh b/scripts/release.sh index 98dbba99..21ea10ae 100644 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -41,6 +41,7 @@ CRATES=( ./lib/domain/libimagwiki ./bin/domain/imag-habit ./bin/domain/imag-diary + ./bin/domain/imag-calendar ./bin/domain/imag-contact ./bin/domain/imag-notes ./bin/domain/imag-bookmark