Skip to content

Commit c3a5c14

Browse files
authored
feat!: Better error reporting in hugr-cli. (#2318)
This PR streamlines the error reporting in `hugr-cli`, displaying error source trails and setting up the tracing crate. For that, some error trait implementations were migrated to `thiserror`. The PR also simplifies the `clap` structures by pulling out the verbosity level from the individual commands. The commands should use tracing macros instead of relying on filtering for verbosity themselves. BREAKING CHANGE: Reorganised `clap` commands and errors in `hugr-cli` which is only visible when it is used as a library.
1 parent 8a17e7c commit c3a5c14

File tree

9 files changed

+243
-149
lines changed

9 files changed

+243
-149
lines changed

Cargo.lock

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hugr-cli/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ derive_more = { workspace = true, features = ["display", "error", "from"] }
2222
hugr = { path = "../hugr", version = "0.20.1" }
2323
serde_json.workspace = true
2424
clio = { workspace = true, features = ["clap-parse"] }
25+
anyhow.workspace = true
26+
thiserror.workspace = true
27+
tracing = "0.1.41"
28+
tracing-subscriber = { version = "0.3.19", features = ["fmt"] }
2529

2630
[lints]
2731
workspace = true

hugr-cli/src/extensions.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Dump standard extensions in serialized form.
2+
use anyhow::Result;
23
use clap::Parser;
34
use hugr::extension::ExtensionRegistry;
45
use std::{io::Write, path::PathBuf};
@@ -25,7 +26,7 @@ impl ExtArgs {
2526
/// Write out the standard extensions in serialized form.
2627
/// Qualified names of extensions used to generate directories under the specified output directory.
2728
/// E.g. extension "foo.bar.baz" will be written to "OUTPUT/foo/bar/baz.json".
28-
pub fn run_dump(&self, registry: &ExtensionRegistry) {
29+
pub fn run_dump(&self, registry: &ExtensionRegistry) -> Result<()> {
2930
let base_dir = &self.outdir;
3031

3132
for ext in registry {
@@ -35,15 +36,17 @@ impl ExtArgs {
3536
}
3637
path.set_extension("json");
3738

38-
std::fs::create_dir_all(path.clone().parent().unwrap()).unwrap();
39+
std::fs::create_dir_all(path.clone().parent().unwrap())?;
3940
// file buffer
40-
let mut file = std::fs::File::create(&path).unwrap();
41+
let mut file = std::fs::File::create(&path)?;
4142

42-
serde_json::to_writer_pretty(&mut file, &ext).unwrap();
43+
serde_json::to_writer_pretty(&mut file, &ext)?;
4344

4445
// write newline, for pre-commit end of file check that edits the file to
4546
// add newlines if missing.
46-
file.write_all(b"\n").unwrap();
47+
file.write_all(b"\n")?;
4748
}
49+
50+
Ok(())
4851
}
4952
}

hugr-cli/src/lib.rs

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@
5757
//! ```
5858
5959
use clap::{Parser, crate_version};
60-
use clap_verbosity_flag::log::Level;
6160
use clap_verbosity_flag::{InfoLevel, Verbosity};
6261
use hugr::envelope::EnvelopeError;
6362
use hugr::package::PackageValidationError;
6463
use std::ffi::OsString;
64+
use thiserror::Error;
6565

6666
pub mod extensions;
6767
pub mod hugr_io;
@@ -73,8 +73,19 @@ pub mod validate;
7373
#[clap(version = crate_version!(), long_about = None)]
7474
#[clap(about = "HUGR CLI tools.")]
7575
#[group(id = "hugr")]
76+
pub struct CliArgs {
77+
/// The command to be run.
78+
#[command(subcommand)]
79+
pub command: CliCommand,
80+
/// Verbosity.
81+
#[command(flatten)]
82+
pub verbose: Verbosity<InfoLevel>,
83+
}
84+
85+
/// The CLI subcommands.
86+
#[derive(Debug, clap::Subcommand)]
7687
#[non_exhaustive]
77-
pub enum CliArgs {
88+
pub enum CliCommand {
7889
/// Validate and visualize a HUGR file.
7990
Validate(validate::ValArgs),
8091
/// Write standard extensions out in serialized form.
@@ -87,40 +98,24 @@ pub enum CliArgs {
8798
}
8899

89100
/// Error type for the CLI.
90-
#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)]
101+
#[derive(Debug, Error)]
91102
#[non_exhaustive]
92103
pub enum CliError {
93104
/// Error reading input.
94-
#[display("Error reading from path: {_0}")]
95-
InputFile(std::io::Error),
105+
#[error("Error reading from path.")]
106+
InputFile(#[from] std::io::Error),
96107
/// Error parsing input.
97-
#[display("Error parsing package: {_0}")]
98-
Parse(serde_json::Error),
99-
#[display("Error validating HUGR: {_0}")]
108+
#[error("Error parsing package.")]
109+
Parse(#[from] serde_json::Error),
110+
#[error("Error validating HUGR.")]
100111
/// Errors produced by the `validate` subcommand.
101-
Validate(PackageValidationError),
102-
#[display("Error decoding HUGR envelope: {_0}")]
112+
Validate(#[from] PackageValidationError),
113+
#[error("Error decoding HUGR envelope.")]
103114
/// Errors produced by the `validate` subcommand.
104-
Envelope(EnvelopeError),
115+
Envelope(#[from] EnvelopeError),
105116
/// Pretty error when the user passes a non-envelope file.
106-
#[display(
117+
#[error(
107118
"Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead."
108119
)]
109120
NotAnEnvelope,
110121
}
111-
112-
/// Other arguments affecting the HUGR CLI runtime.
113-
#[derive(Parser, Debug)]
114-
pub struct OtherArgs {
115-
/// Verbosity.
116-
#[command(flatten)]
117-
pub verbose: Verbosity<InfoLevel>,
118-
}
119-
120-
impl OtherArgs {
121-
/// Test whether a `level` message should be output.
122-
#[must_use]
123-
pub fn verbosity(&self, level: Level) -> bool {
124-
self.verbose.log_level_filter() >= level
125-
}
126-
}

0 commit comments

Comments
 (0)