~matthiasbeyer/imag

bfe15c9157281ef3957001cbe5d54dce9d3ff7fe — Leon Schuermann 1 year, 7 months ago 861af28
imag-git: implement ImagApplication

Signed-off-by: Leon Schuermann <leon@is.currently.online>
3 files changed, 176 insertions(+), 103 deletions(-)

M bin/core/imag-git/Cargo.toml
A bin/core/imag-git/src/bin.rs
R bin/core/imag-git/src/{main.rs => lib.rs}
M bin/core/imag-git/Cargo.toml => bin/core/imag-git/Cargo.toml +8 -0
@@ 23,6 23,7 @@ maintenance                       = { status     = "actively-developed" }
log        = "0.4.6"
toml       = "0.5.1"
toml-query = "0.9.2"
failure    = "0.1.5"

libimagrt    = { version = "0.10.0", path = "../../../lib/core/libimagrt" }
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }


@@ 32,3 33,10 @@ version          = "2.33.0"
default-features = false
features         = ["color", "suggestions", "wrap_help"]

[lib]
name = "libimaggitcmd"
path = "src/lib.rs"

[[bin]]
name = "imag-git"
path = "src/bin.rs"

A bin/core/imag-git/src/bin.rs => bin/core/imag-git/src/bin.rs +39 -0
@@ 0,0 1,39 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2019 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
//

#![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 libimagrt;

simple_imag_application_binary!(libimaggitcmd, ImagGit);

R bin/core/imag-git/src/main.rs => bin/core/imag-git/src/lib.rs +129 -103
@@ 38,8 38,9 @@ extern crate clap;
#[macro_use] extern crate log;
extern crate toml;
extern crate toml_query;
extern crate failure;

#[macro_use] extern crate libimagrt;
extern crate libimagrt;
extern crate libimagerror;

use std::io::Write;


@@ 48,124 49,149 @@ use std::process::Command;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use clap::App;
use failure::Fallible as Result;

use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::setup::generate_runtime_setup;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;

mod ui;

fn main() {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-git",
                                    &version,
                                    "Helper to call git in the store",
                                    ui::build_ui);

    let execute_in_store = rt
        .config()
        .unwrap_or_else(|| {
            error!("No configuration. Please use git yourself, not via imag-git");
            error!("Won't continue without configuration.");
            ::std::process::exit(1);
        })
        .read("git.execute_in_store")
        .unwrap_or_else(|e| {
            error!("Failed to read config setting 'git.execute_in_store'");
            error!("-> {:?}", e);
            ::std::process::exit(1)
        })
        .unwrap_or_else(|| {
            error!("Missing config setting 'git.execute_in_store'");
            ::std::process::exit(1)
        });

    let execute_in_store = match *execute_in_store {
        Value::Boolean(b) => b,
        _ => {
            error!("Type error: 'git.execute_in_store' is not a boolean!");
            ::std::process::exit(1)
        }
    };
/// Marker enum for implementing ImagApplication on
///
/// This is used by binaries crates to execute business logic
/// or to build a CLI completion.
pub enum ImagGit {}
impl ImagApplication for ImagGit {
    fn run(rt: Runtime) -> Result<()> {
        let execute_in_store = rt
            .config()
            .unwrap_or_else(|| {
                error!("No configuration. Please use git yourself, not via imag-git");
                error!("Won't continue without configuration.");
                ::std::process::exit(1);
            })
            .read("git.execute_in_store")
            .unwrap_or_else(|e| {
                error!("Failed to read config setting 'git.execute_in_store'");
                error!("-> {:?}", e);
                ::std::process::exit(1)
            })
            .unwrap_or_else(|| {
                error!("Missing config setting 'git.execute_in_store'");
                ::std::process::exit(1)
            });

        let execute_in_store = match *execute_in_store {
            Value::Boolean(b) => b,
            _ => {
                error!("Type error: 'git.execute_in_store' is not a boolean!");
                ::std::process::exit(1)
            }
        };

    let execpath = if execute_in_store {
        rt.store().path().to_str()
    } else {
        rt.rtp().to_str()
    }
    .map(String::from)
    .unwrap_or_else(|| {
        error!("Cannot parse to string: {:?}", rt.store().path());
        ::std::process::exit(1)
    });


    let mut command = Command::new("git");
    command
        .stdin(::std::process::Stdio::inherit())
        .stdout(::std::process::Stdio::inherit())
        .stderr(::std::process::Stdio::inherit())
        .arg("-C").arg(&execpath);

    let args = rt
        .cli()
        .values_of("")
        .map(|vs| vs.map(String::from).collect())
        .unwrap_or_else(|| vec![]);

    debug!("Adding args = {:?}", args);
    command.args(&args);

    if let (external, Some(ext_m)) = rt.cli().subcommand() {
        command.arg(external);
        let args = ext_m
        let execpath = if execute_in_store {
            rt.store().path().to_str()
        } else {
            rt.rtp().to_str()
        }
        .map(String::from)
            .unwrap_or_else(|| {
                error!("Cannot parse to string: {:?}", rt.store().path());
                ::std::process::exit(1)
            });


        let mut command = Command::new("git");
        command
            .stdin(::std::process::Stdio::inherit())
            .stdout(::std::process::Stdio::inherit())
            .stderr(::std::process::Stdio::inherit())
            .arg("-C").arg(&execpath);

        let args = rt
            .cli()
            .values_of("")
            .map(|vs| vs.map(String::from).collect())
            .unwrap_or_else(|| vec![]);

        debug!("Adding subcommand '{}' and args = {:?}", external, args);
        debug!("Adding args = {:?}", args);
        command.args(&args);
    }
    let mut out = rt.stdout();

    debug!("Calling: {:?}", command);

    match command.spawn().and_then(|mut c| c.wait()) {
        Ok(exit_status) => {
            if !exit_status.success() {
                debug!("git exited with non-zero exit code: {:?}", exit_status);
                let mut err = rt.stderr();
                writeln!(err, "git exited with non-zero exit code")
                    .to_exit_code()
                    .unwrap_or_exit();
                ::std::process::exit(exit_status.code().unwrap_or(1));
            }
            debug!("Successful exit!");
        },

        Err(e) => {
            debug!("Error calling git");
            match e.kind() {
                ErrorKind::NotFound => {
                    writeln!(out, "Cannot find 'git' executable")
                        .to_exit_code()
                        .unwrap_or_exit();
                    ::std::process::exit(1);
                },
                ErrorKind::PermissionDenied => {
                    writeln!(out, "No permission to execute: 'git'")
                        .to_exit_code()
                        .unwrap_or_exit();
                    ::std::process::exit(1);
                },
                _ => {
                    writeln!(out, "Error spawning: {:?}", e)

        match rt.cli().subcommand() {
            (external, Some(ext_m)) => {
                command.arg(external);
                let args = ext_m
                    .values_of("")
                    .map(|vs| vs.map(String::from).collect())
                    .unwrap_or_else(|| vec![]);

                debug!("Adding subcommand '{}' and args = {:?}", external, args);
                command.args(&args);
            },
            _ => {},
        }

        let mut out = rt.stdout();

        debug!("Calling: {:?}", command);

        match command.spawn().and_then(|mut c| c.wait()) {
            Ok(exit_status) => {
                if !exit_status.success() {
                    debug!("git exited with non-zero exit code: {:?}", exit_status);
                    let mut err = rt.stderr();
                    writeln!(err, "git exited with non-zero exit code")
                        .to_exit_code()
                        .unwrap_or_exit();
                    ::std::process::exit(1);
                    ::std::process::exit(exit_status.code().unwrap_or(1));
                }
                debug!("Successful exit!");
            },

            Err(e) => {
                debug!("Error calling git");
                match e.kind() {
                    ErrorKind::NotFound => {
                        let _ = writeln!(out, "Cannot find 'git' executable")
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    },
                    ErrorKind::PermissionDenied => {
                        let _ = writeln!(out, "No permission to execute: 'git'")
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    },
                    _ => {
                        let _ = writeln!(out, "Error spawning: {:?}", e)
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    }
                }
            }
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
        ui::build_ui(app)
    }
}

    fn name() -> &'static str {
        env!("CARGO_PKG_NAME")
    }

    fn description() -> &'static str {
        "Helper to call git in the store"
    }

    fn version() -> &'static str {
        env!("CARGO_PKG_VERSION")
    }
}