diff --git a/bin/core/imag-ids/src/main.rs b/bin/core/imag-ids/src/main.rs index 5dada73e..08beb465 100644 --- a/bin/core/imag-ids/src/main.rs +++ b/bin/core/imag-ids/src/main.rs @@ -42,7 +42,6 @@ extern crate libimagstore; use std::io::Write; use filters::filter::Filter; -use clap::{Arg, App}; use libimagrt::setup::generate_runtime_setup; use libimagerror::trace::MapErrTrace; @@ -50,26 +49,9 @@ use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; use libimagstore::storeid::StoreId; +mod ui; +use ui::build_ui; -/// No special CLI -pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { - app - .arg(Arg::with_name("print-storepath") - .long("with-storepath") - .takes_value(false) - .required(false) - .multiple(false) - .help("Print the storepath for each id")) - - .arg(Arg::with_name("in-collection-filter") - .long("in-collection") - .short("c") - .required(false) - .takes_value(true) - .multiple(true) - .value_names(&["COLLECTION"]) - .help("Filter for ids which are only in these collections")) -} pub struct IsInCollectionsFilter<'a, A>(Option, ::std::marker::PhantomData<&'a str>) where A: AsRef<[&'a str]>; diff --git a/bin/core/imag-ids/src/ui.rs b/bin/core/imag-ids/src/ui.rs new file mode 100644 index 00000000..000539dd --- /dev/null +++ b/bin/core/imag-ids/src/ui.rs @@ -0,0 +1,40 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2018 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}; + +pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app + .arg(Arg::with_name("print-storepath") + .long("with-storepath") + .takes_value(false) + .required(false) + .multiple(false) + .help("Print the storepath for each id")) + + .arg(Arg::with_name("in-collection-filter") + .long("in-collection") + .short("c") + .required(false) + .takes_value(true) + .multiple(true) + .value_names(&["COLLECTION"]) + .help("Filter for ids which are only in these collections")) +} + diff --git a/bin/core/imag-init/src/main.rs b/bin/core/imag-init/src/main.rs index 2ad15725..de809128 100644 --- a/bin/core/imag-init/src/main.rs +++ b/bin/core/imag-init/src/main.rs @@ -50,6 +50,7 @@ use std::process::Command; use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; +use libimagrt::runtime::Runtime; const CONFIGURATION_STR : &'static str = include_str!("../imagrc.toml"); @@ -68,7 +69,10 @@ imagrc.toml fn main() { let version = make_imag_version!(); - let app = ui::build_ui(&version); + let app = ui::build_ui(Runtime::get_default_cli_builder( + "imag-init", + version.as_str(), + "Intializes the imag store, optionally with git")); let matches = app.get_matches(); let mut out = ::std::io::stdout(); diff --git a/bin/core/imag-init/src/ui.rs b/bin/core/imag-init/src/ui.rs index ff985290..7936cc9b 100644 --- a/bin/core/imag-init/src/ui.rs +++ b/bin/core/imag-init/src/ui.rs @@ -19,12 +19,8 @@ use clap::{Arg, App}; -pub fn build_ui<'a>(version: &'a str) -> App<'a, 'a> { - App::new("imag-init") - .version(version) - .author("Matthias Beyer ") - .about("Initialize a ~/.imag repository. Optionally with git") - +pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app .arg(Arg::with_name("devel") .long("dev") .takes_value(false) diff --git a/bin/core/imag/Cargo.toml b/bin/core/imag/Cargo.toml index e0770a6b..b8c4f8b7 100644 --- a/bin/core/imag/Cargo.toml +++ b/bin/core/imag/Cargo.toml @@ -13,7 +13,14 @@ documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.h repository = "https://github.com/matthiasbeyer/imag" homepage = "http://imag-pim.org" -build = "../../../build.rs" +build = "build.rs" + +[build-dependencies] +clap = ">=2.16.1" +version = "2.0" +libimagrt = { path = "../../../lib/core/libimagrt" } +libimagentrytag = { path = "../../../lib/entry/libimagentrytag" } +libimagutil = { path = "../../../lib/etc/libimagutil" } [badges] travis-ci = { repository = "matthiasbeyer/imag" } diff --git a/bin/core/imag/build.rs b/bin/core/imag/build.rs new file mode 100644 index 00000000..96dabc33 --- /dev/null +++ b/bin/core/imag/build.rs @@ -0,0 +1,138 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2018 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 +// + +extern crate clap; +extern crate libimagrt; +extern crate libimagentrytag; +extern crate libimagutil; +#[macro_use] extern crate version; + +use clap::Shell; +use libimagrt::runtime::Runtime; + +mod toplevelbuildscript { + include!("../../../build.rs"); + pub fn build() { + self::main(); + } +} + +/// This macro generates mods with the given '$modulename', +/// whose content is the file given with `$path`. +/// In this case, It is used specifically to include the +/// `ui.rs` files of the imag binaries. +/// The imag project (accidentally?) followed the convention +/// to write a `ui.rs` containing the function +/// `fn build_ui(app : App) -> App`. +/// This macro allows us to use the same named functions by +/// putting them each into their own module. +macro_rules! gen_mods_buildui { + ($(($path:expr, $modulename:ident)$(,)*)*) => ( + $( + mod $modulename { + include!($path); + } + )* + ) +} + +/// This macro reduces boilerplate code. +/// +/// For example: `build_subcommand!("counter", imagcounter)` +/// will result in the following code: +/// ```ignore +/// imagcounter::build_ui(Runtime::get_default_cli_builder( +/// "counter", +/// &version!()[..], +/// "counter")) +/// ``` +/// As for the `&version!()[..]` part, it does not matter +/// which version the subcommand is getting here, as the +/// output of this script is a completion script, which +/// does not contain information about the version at all. +macro_rules! build_subcommand { + ($name:expr, $module:ident) => ( + $module::build_ui(Runtime::get_default_cli_builder($name, &version!()[..], $name)) + ) +} + +// Actually generates the module. +gen_mods_buildui!( + ("../../../bin/core/imag-annotate/src/ui.rs", imagannotate), + ("../../../bin/core/imag-diagnostics/src/ui.rs", imagdiagnostics), + ("../../../bin/core/imag-edit/src/ui.rs", imagedit), + ("../../../bin/core/imag-gps/src/ui.rs", imaggps), + ("../../../bin/core/imag-grep/src/ui.rs", imaggrep), + ("../../../bin/core/imag-ids/src/ui.rs", imagids), + ("../../../bin/core/imag-init/src/ui.rs", imaginit), + ("../../../bin/core/imag-link/src/ui.rs", imaglink), + ("../../../bin/core/imag-mv/src/ui.rs", imagmv), + ("../../../bin/core/imag-ref/src/ui.rs", imagref), + ("../../../bin/core/imag-store/src/ui.rs", imagstore), + ("../../../bin/core/imag-tag/src/ui.rs", imagtag), + ("../../../bin/core/imag-view/src/ui.rs", imagview) + ("../../../bin/domain/imag-bookmark/src/ui.rs", imagbookmark), + ("../../../bin/domain/imag-contact/src/ui.rs", imagcontact), + ("../../../bin/domain/imag-diary/src/ui.rs", imagdiary), + ("../../../bin/domain/imag-habit/src/ui.rs", imaghabit), + ("../../../bin/domain/imag-log/src/ui.rs", imaglog), + ("../../../bin/domain/imag-mail/src/ui.rs", imagmail), + ("../../../bin/domain/imag-notes/src/ui.rs", imagnotes), + ("../../../bin/domain/imag-timetrack/src/ui.rs", imagtimetrack), + ("../../../bin/domain/imag-todo/src/ui.rs", imagtodo), +); + +fn main() { + // Make the `imag`-App... + let mut app = Runtime::get_default_cli_builder( + "imag", + &version!()[..], + "imag") + // and add all the subapps as subcommands. + .subcommand(build_subcommand!("annotate", imagannotate)) + .subcommand(build_subcommand!("diagnostics", imagdiagnostics)) + .subcommand(build_subcommand!("edit", imagedit)) + .subcommand(build_subcommand!("gps", imaggps)) + .subcommand(build_subcommand!("grep", imaggrep)) + .subcommand(build_subcommand!("ids", imagids)) + .subcommand(build_subcommand!("init", imaginit)) + .subcommand(build_subcommand!("link", imaglink)) + .subcommand(build_subcommand!("mv", imagmv)) + .subcommand(build_subcommand!("ref", imagref)) + .subcommand(build_subcommand!("store", imagstore)) + .subcommand(build_subcommand!("tag", imagtag)) + .subcommand(build_subcommand!("view", imagview)) + .subcommand(build_subcommand!("bookmark", imagbookmark)) + .subcommand(build_subcommand!("contact", imagcontact)) + .subcommand(build_subcommand!("diary", imagdiary)) + .subcommand(build_subcommand!("habit", imaghabit)) + .subcommand(build_subcommand!("log", imaglog)) + .subcommand(build_subcommand!("mail", imagmail)) + .subcommand(build_subcommand!("notes", imagnotes)) + .subcommand(build_subcommand!("timetrack", imagtimetrack)) + .subcommand(build_subcommand!("todo", imagtodo)); + + // Actually generates the completion files + app.gen_completions("imag", Shell::Bash, "../../../target/"); + app.gen_completions("imag", Shell::Fish, "../../../target/"); + app.gen_completions("imag", Shell::Zsh, "../../../target/"); + + toplevelbuildscript::build(); +} + diff --git a/bin/core/imag/src/main.rs b/bin/core/imag/src/main.rs index a83cc234..a847af08 100644 --- a/bin/core/imag/src/main.rs +++ b/bin/core/imag/src/main.rs @@ -407,9 +407,6 @@ fn forward_commandline_arguments(m: &ArgMatches, scmd: &mut Vec) { push(Some("editor"), Runtime::arg_editor_name(), m , scmd); - push(Some("generate-commandline-completion"), - Runtime::arg_generate_compl(), m , scmd); - push(None , Runtime::arg_logdest_name() , m , scmd); } diff --git a/build.rs b/build.rs index c2c4ec66..4aa730f0 100644 --- a/build.rs +++ b/build.rs @@ -19,7 +19,7 @@ use std::process::Command; fn main() { - let profile = String::from(std::env::var("PROFILE").unwrap()); + let profile = String::from(::std::env::var("PROFILE").unwrap()); let git_version = if profile == "debug" { let output = Command::new("git") .args(&["describe", "HEAD"]) diff --git a/lib/core/libimagrt/src/runtime.rs b/lib/core/libimagrt/src/runtime.rs index c0e8488f..a51fe8c5 100644 --- a/lib/core/libimagrt/src/runtime.rs +++ b/lib/core/libimagrt/src/runtime.rs @@ -111,27 +111,14 @@ impl<'a> Runtime<'a> { Runtime::_new(cli_app, matches, config) } - fn _new(mut cli_app: C, matches: ArgMatches<'a>, config: Option) + fn _new(cli_app: C, matches: ArgMatches<'a>, config: Option) -> Result, RuntimeError> where C: Clone + CliSpec<'a> + InternalConfiguration { - use std::io::stdout; - use clap::Shell; - if cli_app.enable_logging() { Runtime::init_logger(&matches, config.as_ref()) } - match matches.value_of(Runtime::arg_generate_compl()) { - Some(shell) => { - debug!("Generating shell completion script, writing to stdout"); - let shell = shell.parse::().unwrap(); // clap has our back here. - let appname = String::from(cli_app.name()); - cli_app.completions(appname, shell, &mut stdout()); - }, - _ => debug!("Not generating shell completion script"), - } - let rtp = get_rtp_match(&matches); let storepath = matches.value_of(Runtime::arg_storepath_name()) @@ -242,14 +229,6 @@ impl<'a> Runtime<'a> { .required(false) .takes_value(true)) - .arg(Arg::with_name(Runtime::arg_generate_compl()) - .long("generate-commandline-completion") - .help("Generate the commandline completion for bash or zsh or fish") - .required(false) - .takes_value(true) - .value_name("SHELL") - .possible_values(&["bash", "fish", "zsh"])) - .arg(Arg::with_name(Runtime::arg_logdest_name()) .long(Runtime::arg_logdest_name()) .help("Override the logging destinations from the configuration: values can be seperated by ',', a value of '-' marks the stderr output, everything else is expected to be a path") @@ -320,11 +299,6 @@ impl<'a> Runtime<'a> { "editor" } - /// Get the argument name for generating the completion - pub fn arg_generate_compl() -> &'static str { - "generate-completion" - } - /// Extract the Store object from the Runtime object, destroying the Runtime object /// /// # Warning