From 6ac6db57d11eeb2393fe0771db630899e9ea326c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 25 Jul 2019 20:23:51 +0200 Subject: Add imag-timetrack shell This patch adds a subcommand to imag-timetrack which allows a user to start a $SHELL and start a timetracking with it and as soon as the shell exits, the timetracking is stopped. Signed-off-by: Matthias Beyer --- bin/domain/imag-timetrack/src/main.rs | 3 + bin/domain/imag-timetrack/src/shell.rs | 110 +++++++++++++++++++++++++++++++++ bin/domain/imag-timetrack/src/stop.rs | 2 +- bin/domain/imag-timetrack/src/ui.rs | 27 ++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 bin/domain/imag-timetrack/src/shell.rs diff --git a/bin/domain/imag-timetrack/src/main.rs b/bin/domain/imag-timetrack/src/main.rs index 2862b6d7..1c06e845 100644 --- a/bin/domain/imag-timetrack/src/main.rs +++ b/bin/domain/imag-timetrack/src/main.rs @@ -55,6 +55,7 @@ mod cont; mod day; mod list; mod month; +mod shell; mod start; mod stop; mod track; @@ -66,6 +67,7 @@ use crate::cont::cont; use crate::day::day; use crate::list::{list, list_impl}; use crate::month::month; +use crate::shell::shell; use crate::start::start; use crate::stop::stop; use crate::track::track; @@ -91,6 +93,7 @@ fn main() { "day" => day(&rt), "list" => list(&rt), "month" => month(&rt), + "shell" => shell(&rt), "start" => start(&rt), "stop" => stop(&rt), "track" => track(&rt), diff --git a/bin/domain/imag-timetrack/src/shell.rs b/bin/domain/imag-timetrack/src/shell.rs new file mode 100644 index 00000000..97d659a8 --- /dev/null +++ b/bin/domain/imag-timetrack/src/shell.rs @@ -0,0 +1,110 @@ +// +// 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 std::env; +use std::process::Command; + +use filters::filter::Filter; + +use libimagerror::exit::ExitUnwrap; +use libimagerror::iter::TraceIterator; +use libimagerror::trace::MapErrTrace; +use libimagerror::trace::trace_error; +use libimagrt::runtime::Runtime; +use libimagtimetrack::iter::filter::has_one_of_tags; +use libimagtimetrack::store::TimeTrackStore; +use libimagtimetrack::tag::TimeTrackingTag; +use libimagtimetrack::timetracking::TimeTracking; +use libimagutil::warn_result::*; + +pub fn shell(rt: &Runtime) -> i32 { + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + let start = ::chrono::offset::Local::now().naive_local(); + let tags = cmd.values_of("tags") + .unwrap() // enforced by clap + .map(String::from) + .map(TimeTrackingTag::from) + .collect::>(); + + let mut shellcmd = { + let mkshell = |s: String| { + let mut cmd = Command::new(s); + cmd.stdin(::std::process::Stdio::inherit()); + cmd.stdout(::std::process::Stdio::inherit()); + cmd.stderr(::std::process::Stdio::inherit()); + cmd + }; + + if let Some(s) = cmd.value_of("shell") { + mkshell(s.to_owned()) + } else { + env::var("SHELL") + .map(|s| mkshell(s)) + .map_err(|e| match e { + env::VarError::NotPresent => { + error!("No $SHELL variable in environment, cannot work!"); + ::std::process::exit(1) + }, + env::VarError::NotUnicode(_) => { + error!("$SHELL variable is not unicode, cannot work!"); + ::std::process::exit(1) + } + }) + .unwrap() + } + }; + + for tag in tags.iter() { + match rt.store().create_timetracking_at(&start, tag) { + Err(e) => trace_error(&e), + Ok(entry) => { + let _ = rt.report_touched(entry.get_location()).unwrap_or_exit(); + } + } + } + + let exit_code = match shellcmd.status() { + Ok(estat) => estat.code().unwrap_or(0), + Err(e) => { + error!("Error starting shell: {:?}", e); + ::std::process::exit(2) + }, + }; + + let stop = ::chrono::offset::Local::now().naive_local(); + let filter = has_one_of_tags(&tags); + rt.store() + .get_timetrackings() + .map_warn_err_str("Getting timetrackings failed") + .map_err_trace_exit_unwrap() + .trace_unwrap() + .filter(|e| filter.filter(e)) + .for_each(|mut elem| if let Err(e) = elem.set_end_datetime(stop.clone()) { + trace_error(&e) + } else { + debug!("Setting end time worked: {:?}", elem); + let _ = rt.report_touched(elem.get_location()).unwrap_or_exit(); + }); + + ::std::process::exit(exit_code) +} + + diff --git a/bin/domain/imag-timetrack/src/stop.rs b/bin/domain/imag-timetrack/src/stop.rs index f5df726c..8bcd344b 100644 --- a/bin/domain/imag-timetrack/src/stop.rs +++ b/bin/domain/imag-timetrack/src/stop.rs @@ -98,7 +98,7 @@ pub fn stop(rt: &Runtime) -> i32 { 1 } Ok(_) => { - format!("Setting end time worked: {:?}", elem); + debug!("Setting end time worked: {:?}", elem); let _ = rt.report_touched(elem.get_location()).unwrap_or_exit(); acc } diff --git a/bin/domain/imag-timetrack/src/ui.rs b/bin/domain/imag-timetrack/src/ui.rs index a654a819..2803ec9b 100644 --- a/bin/domain/imag-timetrack/src/ui.rs +++ b/bin/domain/imag-timetrack/src/ui.rs @@ -183,4 +183,31 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("Limit to certain tags")) ) + .subcommand(SubCommand::with_name("shell") + .about("Start a shell and start timetracking, stop when shell exits") + .version("0.1") + .long_about(r#" + Tries to find the current shell via $SHELL. If none is found, this aborts operation and returns 1. + If a shell is found in the environment variables, the time tracking is created and the shell started. + As soon as the shell exits (no matter what exit code), the timetracking is stopped. + The command exits with the exit code of the shell it started. If there is no exit code, this exits with 0. + If there was a failure during setting the end-time, the command exits with the exit code of the shell anyways, but prints error information. + If the command for the shell could not be executed, this fails with 2. + "#) + + .arg(Arg::with_name("shell") + .long("shell") + .short("s") + .required(false) + .multiple(false) + .takes_value(true) + .help("Shell to start, defaults to $SHELL")) + + .arg(Arg::with_name("tags") + .index(1) + .required(true) + .multiple(true) + .help("Tags to start")) + ) + } -- cgit v1.2.3