Skip to content

Commit

Permalink
fixup! Introduce support for user-provided extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
d-e-s-o committed Sep 20, 2020
1 parent 92ec9fc commit eef8204
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 33 deletions.
8 changes: 2 additions & 6 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,13 +1185,9 @@ pub fn extension(ctx: &mut Context<'_>, args: Vec<ffi::OsString>) -> anyhow::Res
if out.status.success() {
Ok(())
} else if let Some(rc) = out.status.code() {
anyhow::bail!(
"Extension {} failed with error status {}",
ext_path.display(),
rc
)
Err(anyhow::Error::new(crate::DirectExitError(rc)))
} else {
anyhow::bail!("Extension {} indicated a failure", ext_path.display())
Err(anyhow::Error::new(crate::DirectExitError(1)))
}
}

Expand Down
48 changes: 37 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ mod pinentry;
mod tests;

use std::env;
use std::error;
use std::ffi;
use std::fmt;
use std::io;
use std::process;
use std::str;
Expand All @@ -76,6 +78,28 @@ const NITROCLI_NEW_ADMIN_PIN: &str = "NITROCLI_NEW_ADMIN_PIN";
const NITROCLI_NEW_USER_PIN: &str = "NITROCLI_NEW_USER_PIN";
const NITROCLI_PASSWORD: &str = "NITROCLI_PASSWORD";

/// A special error type that indicates the desire to exit directly,
/// without additional error reporting.
///
/// This error is mostly used by the extension support code so that we
/// are able to mirror the extension's exit code while preserving our
/// context logic and the fairly isolated testing it enables.
struct DirectExitError(i32);

impl fmt::Debug for DirectExitError {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
unreachable!()
}
}

impl fmt::Display for DirectExitError {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
unreachable!()
}
}

impl error::Error for DirectExitError {}

/// Parse the command-line arguments and execute the selected command.
fn handle_arguments(ctx: &mut Context<'_>, args: Vec<String>) -> anyhow::Result<()> {
use structopt::StructOpt;
Expand Down Expand Up @@ -152,16 +176,21 @@ impl<'io> Context<'io> {
}
}

fn run<'ctx, 'io: 'ctx>(ctx: &'ctx mut Context<'io>, args: Vec<String>) -> i32 {
match handle_arguments(ctx, args) {
Ok(()) => 0,
Err(err) => {
let _ = eprintln!(ctx, "{:?}", err);
1
}
fn evaluate_err(err: anyhow::Error, stderr: &mut dyn io::Write) -> i32 {
if let Some(err) = err.root_cause().downcast_ref::<DirectExitError>() {
err.0
} else {
let _ = writeln!(stderr, "{:?}", err);
1
}
}

fn run<'ctx, 'io: 'ctx>(ctx: &'ctx mut Context<'io>, args: Vec<String>) -> i32 {
handle_arguments(ctx, args)
.map(|()| 0)
.unwrap_or_else(|err| evaluate_err(err, ctx.stderr))
}

fn main() {
use std::io::Write;

Expand All @@ -176,10 +205,7 @@ fn main() {

run(ctx, args)
}
Err(err) => {
let _ = writeln!(stderr, "{:?}", err);
1
}
Err(err) => evaluate_err(err, &mut stderr),
};

// We exit the process the hard way below. The problem is that because
Expand Down
11 changes: 1 addition & 10 deletions src/redefine.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// redefine.rs

// Copyright (C) 2019 The Nitrocli Developers
// Copyright (C) 2019-2020 The Nitrocli Developers
// SPDX-License-Identifier: GPL-3.0-or-later

// A replacement of the standard println!() macro that requires an
Expand All @@ -19,12 +19,3 @@ macro_rules! print {
write!($ctx.stdout, $($arg)*)
};
}

macro_rules! eprintln {
($ctx:expr) => {
writeln!($ctx.stderr, "")
};
($ctx:expr, $($arg:tt)*) => {
writeln!($ctx.stderr, $($arg)*)
};
}
21 changes: 15 additions & 6 deletions src/tests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,22 @@ sys.exit(42);
}

let path = ext_dir.path().as_os_str().to_os_string();
let err = Nitrocli::new().path(path).handle(&["ext"]).unwrap_err();
let mut ncli = Nitrocli::new().path(path);

let err = ncli.handle(&["ext"]).unwrap_err();
// The extension is responsible for printing any error messages.
// Nitrocli is expected not to mess with them, including adding
// additional information.
if let Some(crate::DirectExitError(rc)) = err.downcast_ref::<crate::DirectExitError>() {
assert_eq!(*rc, 42)
} else {
panic!("encountered unexpected error: {:#}", err)
}

let expected = format!(
"Extension {} failed with error status 42",
ext_dir.path().join("nitrocli-ext").display()
);
assert_eq!(err.to_string(), expected);
let (rc, out, err) = ncli.run(&["ext"]);
assert_eq!(rc, 42);
assert_eq!(out, b"");
assert_eq!(err, b"");
Ok(())
}

Expand Down

0 comments on commit eef8204

Please sign in to comment.