Skip to content

Commit

Permalink
Merge pull request #12 from AhmedSoliman/pr12
Browse files Browse the repository at this point in the history
0.1.2 with anyhow error formatting
  • Loading branch information
AhmedSoliman authored Mar 19, 2024
2 parents 3b40f69 + 355b064 commit 57f1ae4
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
# https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
strategy:
matrix:
msrv: [1.70.0]
msrv: [1.74.0]
name: ubuntu / ${{ matrix.msrv }}
steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ resolver = "2"
members = ["cling", "cling-derive"]

[workspace.package]
version = "0.1.1"
version = "0.1.2"
edition = "2021"
authors = ["Ahmed Farghal <me@asoli.dev>"]
license = "Apache-2.0 OR MIT"
rust-version = "1.70.0" # MSRV
rust-version = "1.74.0" # MSRV
keywords = ["click", "cli", "framework"]
repository = "https://github.com/AhmedSoliman/cling"

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ For more details, see:
- [docs.rs](https://docs.rs/cling/latest/cling/)
- [examples](examples/)

*Compiler support: [requires `rustc` 1.70+][msrv]*
*Compiler support: [requires `rustc` 1.74+][msrv]*

[msrv]: #supported-rust-versions

Expand All @@ -56,6 +56,7 @@ edition = "2021"

[dependencies]
clap = { version = "4.4.1", features = ["derive", "env"] }
cling = { version = "0.1" }
tokio = { version = "1.13.0", features = ["full"] }
```

Expand Down Expand Up @@ -341,7 +342,7 @@ Beep beep!

# Supported Rust Versions

Cling's minimum supported rust version is `1.70.0`.
Cling's minimum supported rust version is `1.74.0`.

# License

Expand Down
2 changes: 1 addition & 1 deletion cling-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version = "1.70.0"
rust-version.workspace = true
categories = [
"command-line-interface",
"development-tools::procedural-macro-helpers",
Expand Down
19 changes: 12 additions & 7 deletions cling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version = "1.70.0"
rust-version.workspace = true
categories = ["command-line-interface"]
include = ["**/*.rs", "../README.md", "../LICENSE-*", "../examples"]
readme = "../README.md"

[dependencies]
cling-derive = { path = "../cling-derive", version = "0.1", optional = true }
clap = { version = "4.3.21", default-features = false, features = [
clap = { version = "4", default-features = false, features = [
"std",
"derive",
] }
anyhow = { version = "1.0" }
async-trait = { version = "0.1.70" }
async-trait = { version = "0.1.77" }
indoc = { version = "2.0" }
itertools = { version = "0.12.1" }
rustversion = "1.0.14"
shlex = { version = "1.1.0", optional = true }
shlex = { version = "1.3.0", optional = true }
static_assertions = { workspace = true }
termcolor = { version = "1.2" }
termcolor = { version = "1.4" }
tracing = { version = "0.1.37", features = ["log"] }


Expand All @@ -40,8 +41,8 @@ colored = { version = "2.0" }
# Use clap with default features in tests
clap = { version = "4.3.21", default-features = true, features = ["derive"] }
# For testing collecting external types
clap-verbosity-flag = { version = "2.0.1" }
env_logger = { version = "0.11.0" }
clap-verbosity-flag = { version = "2.2.0" }
env_logger = { version = "0.11.3" }
log = { version = "0.4.20" }

[build-dependencies]
Expand Down Expand Up @@ -82,6 +83,10 @@ required-features = ["shlex"]
name = "many-handlers"
path = "../examples/many_handlers.rs"

[[example]]
name = "errors"
path = "../examples/errors.rs"

[package.metadata.docs.rs]
all-features = true
default-target = "x86_64-unknown-linux-gnu"
Expand Down
43 changes: 39 additions & 4 deletions cling/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter};
use std::io::Write;

use clap::CommandFactory;
use itertools::{Itertools, Position};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::prelude::ClingFinished;
Expand Down Expand Up @@ -101,10 +102,10 @@ impl Display for CliError {
write!(f, "Error: {}", e)
}
| CliError::Other(e) => {
write!(f, "Error: {}", e)
write!(f, "Error: {:#}", e)
}
| CliError::OtherWithCode(e, _) => {
write!(f, "Error: {}", e)
write!(f, "Error: {:#}", e)
}
| CliError::InputString => {
write!(f, "Input string cannot be parsed as UNIX shell command")
Expand Down Expand Up @@ -154,10 +155,10 @@ impl CliError {
print_formatted_error(&mut stderr, "", e)
}
| CliError::Other(e) => {
print_formatted_error(&mut stderr, "Error: ", &e.to_string())
print_anyhow_error(&mut stderr, "Error: ", e)
}
| CliError::OtherWithCode(e, _) => {
print_formatted_error(&mut stderr, "Error: ", &e.to_string())
print_anyhow_error(&mut stderr, "Error: ", e)
}
| e @ CliError::InputString => {
print_formatted_error(&mut stderr, "", &e.to_string())
Expand Down Expand Up @@ -220,6 +221,40 @@ fn print_formatted_error(
Ok(())
}

fn print_anyhow_error(
f: &mut StandardStream,
heading: &str,
err: &anyhow::Error,
) -> std::io::Result<()> {
f.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
write!(f, "{}", heading)?;
f.reset()?;
writeln!(f, "{}", err)?;
err.chain()
.skip(1)
.with_position()
.for_each(|(position, cause)| {
if position == Position::First {
let _ =
f.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)));
let _ = writeln!(f, "");

Check warning on line 240 in cling/src/error.rs

View workflow job for this annotation

GitHub Actions / clippy

empty string literal in `writeln!`

warning: empty string literal in `writeln!` --> cling/src/error.rs:240:25 | 240 | let _ = writeln!(f, ""); | ^^^^^^^^^^----^ | | | help: remove the empty string | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string = note: `#[warn(clippy::writeln_empty_string)]` on by default
let _ = writeln!(f, "Caused by:");
let _ = f.reset();
}
let symbol = if position == Position::Last {
"└─"
} else {
"├─"
};
let _ =
f.set_color(ColorSpec::new().set_italic(true).set_dimmed(true));
let _ = write!(f, " {} ", symbol);
let _ = f.reset();
let _ = writeln!(f, "{}", cause);
});
Ok(())
}

pub(crate) fn format_clap_error<I: CommandFactory>(
err: clap::Error,
) -> clap::Error {
Expand Down
26 changes: 26 additions & 0 deletions examples/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use cling::prelude::*;

#[derive(Run, Parser, Debug, Clone)]
#[cling(run = "run")]
pub struct App {}

// handlers can be sync or async, cling will handle this transparently.
async fn run() -> anyhow::Result<()> {
let err1 = std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"Fatal disk IO Error!",
);
let err2 = anyhow::Error::new(err1).context("Trying to read a file");
let err3 = err2.context("Can't load application");
let err4 = err3.context("App level error");
Err(err4)
}

#[tokio::main]
async fn main() -> ClingFinished<App> {
env_logger::builder().init();
// Cling::parse().run().await
// Or, return ClingFinished<T> to let cling handle error printing and exit
// code in a more convenient way.
Cling::parse_and_run().await
}

0 comments on commit 57f1ae4

Please sign in to comment.