workmn 0.0.5

Manages the lifecycle of projects on the local machine
Documentation
use super::{ActionContext, ActionResult, CliAction};
use failure::ResultExt;
use log::error;
use std::ffi::OsString;
use workmn::error::{InvalidProperty, ProjectNotFound};

#[cfg(unix)]
use std::os::unix::process::CommandExt;

pub struct Command {}

impl<'a> CliAction for Command {
    fn dispatch(context: ActionContext) -> ActionResult {
        let project_name = context.matches.value_of("project").unwrap();
        let config = context.unwrap_conf();

        let project = config.project(project_name);

        if project.is_none() {
            return Err(ProjectNotFound::new(&project_name).into());
        }

        let project = project.unwrap();

        let path = project.path();

        if path.is_none() {
            return Err(InvalidProperty::new(&project_name, "path").into());
        }

        let path = path.unwrap();

        std::env::set_current_dir(&path).context("Switching to project path")?;

        let shell = user_shell();

        if shell.is_none() {
            return Err(failure::err_msg("Could not determine the correct shell"));
        }

        let shell = shell.unwrap();

        let command = std::process::Command::new(shell);

        if context.matches.is_present("spawn") {
            try_spawn(command)
        } else {
            try_exec(command)
        }
    }
}

fn try_spawn(mut command: std::process::Command) -> ActionResult {
    match command.status() {
        Ok(status) => {
            if status.success() {
                Ok(())
            } else {
                Err(failure::err_msg(format!(
                    "process exited with status code {}",
                    status
                )))
            }
        }
        Err(err) => {
            error!("unable to start shell: {}", &err);
            Err(err.into())
        }
    }
}

#[cfg(not(unix))]
use self::try_spawn as try_exec;

#[cfg(unix)]
fn try_exec(mut command: std::process::Command) -> ActionResult {
    let error = command.exec();
    error!("unable to start shell: {}", error);
    Err(error.into())
}

fn user_shell() -> Option<OsString> {
    if let Some(shell) = std::env::var_os("SHELL") {
        Some(shell)
    } else {
        // TODO: parse some other source of shell info?
        None
    }
}