Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
bin: Improve error handling
Matthias Beyer committed 8 months ago
commit 7a1ba8595ae408c381b5e8a2fb35efdc4ec20f45
parent a2de36d
2 files changed +43 -27
modified bin/main.rs
@@ -11,9 +11,10 @@ mod ui;

use std::ffi::OsString;
use std::io;
-
use std::io::Write;
use std::{iter, process};

+
use thiserror::Error;
+

use radicle::version::Version;

use radicle_cli::terminal as cli_term;
@@ -21,6 +22,8 @@ use radicle_cli::terminal as cli_term;
use commands::*;
use terminal as term;

+
use crate::terminal::ForwardError;
+

pub const NAME: &str = "rad-tui";
pub const DESCRIPTION: &str = "Radicle terminal interfaces";
pub const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -33,6 +36,18 @@ pub const VERSION: Version = Version {
    timestamp: TIMESTAMP,
};

+
#[derive(Error, Debug)]
+
pub enum Error {
+
    #[error("{0}")]
+
    Forward(#[from] term::ForwardError),
+
    #[error("{0}")]
+
    Args(#[from] lexopt::Error),
+
    #[error("{0}")]
+
    Json(#[from] serde_json::Error),
+
    #[error("{0}")]
+
    Other(#[from] anyhow::Error),
+
}
+

#[derive(Debug)]
enum CommandName {
    Other(Vec<OsString>),
@@ -54,18 +69,21 @@ enum Command {
}

fn main() {
-
    match parse_args().map_err(Some).and_then(run) {
+
    match parse_args().and_then(run) {
        Ok(_) => process::exit(0),
        Err(err) => {
-
            if let Some(err) = err {
-
                radicle_term::error(format!("rad-tui: {err}"));
+
            match err {
+
                // Do not print an additonal error message if `rad` itself
+
                // already printed its error(s).
+
                Error::Forward(ForwardError::RadInternal) => {}
+
                _ => radicle_term::error(format!("rad-tui: {err}")),
            }
            process::exit(1);
        }
    }
}

-
fn parse_args() -> anyhow::Result<Command> {
+
fn parse_args() -> anyhow::Result<Command, Error> {
    use lexopt::prelude::*;

    let mut parser = lexopt::Parser::from_env();
@@ -100,7 +118,7 @@ fn parse_args() -> anyhow::Result<Command> {
                    }
                }
            }
-
            _ => return Err(anyhow::anyhow!(arg.unexpected())),
+
            _ => return Err(arg.unexpected().into()),
        }
    }

@@ -150,15 +168,13 @@ fn print_help() -> anyhow::Result<()> {
    tui_help::run(Default::default(), cli_term::DefaultContext)
}

-
fn run(command: Command) -> Result<(), Option<anyhow::Error>> {
+
fn run(command: Command) -> Result<(), Error> {
    match command {
        Command::Version { json } => {
            let mut stdout = io::stdout();
            if json {
-
                VERSION
-
                    .write_json(&mut stdout)
-
                    .map_err(|e| Some(e.into()))?;
-
                writeln!(&mut stdout).ok();
+
                VERSION.write_json(&mut stdout)?;
+
                println!();
            } else {
                println!("rad-tui {} ({})", VERSION.version, VERSION.commit);
            }
@@ -182,7 +198,7 @@ fn run(command: Command) -> Result<(), Option<anyhow::Error>> {
    Ok(())
}

-
fn run_other(command: Option<&str>, args: &[OsString]) -> Result<(), Option<anyhow::Error>> {
+
fn run_other(command: Option<&str>, args: &[OsString]) -> Result<(), Error> {
    match command {
        Some("issue") => {
            term::run_command_args::<tui_issue::Options, _>(
@@ -205,7 +221,7 @@ fn run_other(command: Option<&str>, args: &[OsString]) -> Result<(), Option<anyh
                args.to_vec(),
            );
        }
-
        command => term::run_rad(command, args),
+
        command => term::run_rad(command, args).map_err(|err| err.into()),
    }
}

modified bin/terminal.rs
@@ -1,36 +1,36 @@
use std::ffi::OsString;
-
use std::io::ErrorKind;
use std::process;

-
use anyhow::anyhow;
+
use thiserror::Error;

use radicle_cli::terminal;
use radicle_cli::terminal::args;
use radicle_cli::terminal::io;
use radicle_cli::terminal::{Args, Command, DefaultContext, Error, Help};

-
fn _run_rad(args: &[OsString]) -> Result<(), Option<anyhow::Error>> {
+
#[derive(Error, Debug)]
+
pub enum ForwardError {
+
    #[error("an internal error occured while executing 'rad'")]
+
    RadInternal,
+
    #[error("an I/O error occured while trying to forward command to 'rad': {0}")]
+
    Io(#[from] std::io::Error),
+
}
+

+
fn _run_rad(args: &[OsString]) -> Result<(), ForwardError> {
    let status = process::Command::new("rad").args(args).status();

    match status {
        Ok(status) => {
            if !status.success() {
-
                return Err(None);
-
            }
-
        }
-
        Err(err) => {
-
            if let ErrorKind::NotFound = err.kind() {
-
                return Err(Some(anyhow!("'rad' was not found.",)));
-
            } else {
-
                return Err(Some(err.into()));
+
                return Err(ForwardError::RadInternal);
            }
+
            Ok(())
        }
+
        Err(err) => Err(err.into()),
    }
-

-
    Ok(())
}

-
pub fn run_rad(command: Option<&str>, args: &[OsString]) -> Result<(), Option<anyhow::Error>> {
+
pub fn run_rad(command: Option<&str>, args: &[OsString]) -> Result<(), ForwardError> {
    let args = if let Some(command) = command {
        [vec![command.into()], args.to_vec()].concat()
    } else {