]> quire.kejadlen.dev Git - quire.git/commitdiff
Switch from color-eyre to miette for error reporting
authorAlpha Chen <alpha@kejadlen.dev>
Fri, 24 Apr 2026 13:27:33 +0000 (06:27 -0700)
committerAlpha Chen <alpha@kejadlen.dev>
Fri, 24 Apr 2026 14:30:44 +0000 (07:30 -0700)
Miette provides better diagnostic output and integrates with thiserror
via the Diagnostic derive. Library errors now derive both Error and
Diagnostic. Binary uses miette::Result with into_diagnostic() for
third-party errors.

Assisted-by: GLM-5.1 via pi
Cargo.lock
Cargo.toml
src/bin/quire/commands/exec.rs
src/bin/quire/commands/serve.rs
src/bin/quire/main.rs
src/error.rs

index 36e0fda986e1e2f6474e1186d497bed8f63c30bf..1dbce2f9663324d09da8dd3423ad67bf4890db27 100644 (file)
@@ -118,6 +118,15 @@ dependencies = [
  "windows-link",
 ]
 
+[[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
 [[package]]
 name = "bitflags"
 version = "2.11.1"
@@ -196,33 +205,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
 
-[[package]]
-name = "color-eyre"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d"
-dependencies = [
- "backtrace",
- "color-spantrace",
- "eyre",
- "indenter",
- "once_cell",
- "owo-colors",
- "tracing-error",
-]
-
-[[package]]
-name = "color-spantrace"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427"
-dependencies = [
- "once_cell",
- "owo-colors",
- "tracing-core",
- "tracing-error",
-]
-
 [[package]]
 name = "colorchoice"
 version = "1.0.5"
@@ -251,16 +233,6 @@ dependencies = [
  "windows-sys",
 ]
 
-[[package]]
-name = "eyre"
-version = "0.6.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
-dependencies = [
- "indenter",
- "once_cell",
-]
-
 [[package]]
 name = "fastrand"
 version = "2.4.1"
@@ -337,12 +309,6 @@ version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
 
-[[package]]
-name = "indenter"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
-
 [[package]]
 name = "indexmap"
 version = "2.14.0"
@@ -355,6 +321,12 @@ dependencies = [
  "serde_core",
 ]
 
+[[package]]
+name = "is_ci"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
+
 [[package]]
 name = "is_terminal_polyfill"
 version = "1.70.2"
@@ -421,6 +393,36 @@ version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
 
+[[package]]
+name = "miette"
+version = "7.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
+dependencies = [
+ "backtrace",
+ "backtrace-ext",
+ "cfg-if",
+ "miette-derive",
+ "owo-colors",
+ "supports-color",
+ "supports-hyperlinks",
+ "supports-unicode",
+ "terminal_size",
+ "textwrap",
+ "unicode-width 0.1.14",
+]
+
+[[package]]
+name = "miette-derive"
+version = "7.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "miniz_oxide"
 version = "0.8.9"
@@ -577,8 +579,8 @@ dependencies = [
  "assert_cmd",
  "clap",
  "clap_complete",
- "color-eyre",
  "fs-err",
+ "miette",
  "predicates",
  "shell-words",
  "tempfile",
@@ -761,6 +763,27 @@ version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
+[[package]]
+name = "supports-color"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6"
+dependencies = [
+ "is_ci",
+]
+
+[[package]]
+name = "supports-hyperlinks"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91"
+
+[[package]]
+name = "supports-unicode"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2"
+
 [[package]]
 name = "syn"
 version = "2.0.117"
@@ -785,12 +808,32 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "terminal_size"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
+dependencies = [
+ "rustix",
+ "windows-sys",
+]
+
 [[package]]
 name = "termtree"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
 
+[[package]]
+name = "textwrap"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
+dependencies = [
+ "unicode-linebreak",
+ "unicode-width 0.2.2",
+]
+
 [[package]]
 name = "thiserror"
 version = "2.0.18"
@@ -880,16 +923,6 @@ dependencies = [
  "valuable",
 ]
 
-[[package]]
-name = "tracing-error"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
-dependencies = [
- "tracing",
- "tracing-subscriber",
-]
-
 [[package]]
 name = "tracing-log"
 version = "0.2.0"
@@ -925,6 +958,24 @@ version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
 
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
 [[package]]
 name = "unicode-xid"
 version = "0.2.6"
index f8da1e0fbb395796c52b40c756458e2c53ed2efc..92fa74729b099d34673e4cc22a59a06f986dca3f 100644 (file)
@@ -10,7 +10,7 @@ path = "src/bin/quire/main.rs"
 [dependencies]
 clap = { version = "*", features = ["derive", "env"] }
 clap_complete = "*"
-color-eyre = "*"
+miette = { version = "*", features = ["fancy"] }
 fs-err = "*"
 shell-words = "*"
 thiserror = "*"
index 8aaacee4297562c7a8f6805acfbfcecaddc9fd95..0b2ad73daa6e6da53a3eedf422641a811dd4dd3c 100644 (file)
@@ -2,8 +2,7 @@ use std::os::unix::process::CommandExt;
 use std::path::Path;
 use std::process::Command;
 
-use color_eyre::eyre::{self, Context};
-use color_eyre::Result;
+use miette::{miette, Context, IntoDiagnostic, Result};
 
 const GIT_COMMANDS: &[&str] = &["git-receive-pack", "git-upload-pack", "git-upload-archive"];
 
@@ -19,27 +18,32 @@ pub async fn run(command: Vec<String>) -> Result<()> {
         command.join(" ")
     };
 
-    let words = shell_words::split(&input).context("failed to parse command")?;
+    let words = shell_words::split(&input)
+        .into_diagnostic()
+        .context("failed to parse command")?;
 
     if words.is_empty() {
-        eyre::bail!("no command provided");
+        return Err(miette!("no command provided"));
     }
 
     let git_cmd = &words[0];
 
     if !GIT_COMMANDS.contains(&git_cmd.as_str()) {
-        eyre::bail!("unsupported command: {git_cmd}");
+        return Err(miette!("unsupported command: {git_cmd}"));
     }
 
     if words.len() != 2 {
-        eyre::bail!("expected usage: {git_cmd} '<repo>', got {} arguments", words.len() - 1);
+        return Err(miette!(
+            "expected usage: {git_cmd} '<repo>', got {} arguments",
+            words.len() - 1
+        ));
     }
 
     let repo = validate_repo_path(&words[1])?;
 
     let repo_dir = Path::new(REPOS_DIR).join(&repo);
     if !repo_dir.is_dir() {
-        eyre::bail!("repository not found: {repo}");
+        return Err(miette!("repository not found: {repo}"));
     }
 
     tracing::info!(%git_cmd, %repo, "dispatching git command");
@@ -47,7 +51,7 @@ pub async fn run(command: Vec<String>) -> Result<()> {
     let repo_dir = Path::new(REPOS_DIR).join(&repo);
     let err = Command::new(git_cmd).arg(".").current_dir(&repo_dir).exec();
 
-    Err(eyre::eyre!("exec failed: {err}"))
+    Err(miette!("exec failed: {err}"))
 }
 
 /// Validate a repo path argument from the SSH protocol.
@@ -59,19 +63,19 @@ fn validate_repo_path(raw: &str) -> Result<String> {
     let path = raw.trim_start_matches('/');
 
     if path.is_empty() {
-        eyre::bail!("empty repository path");
+        return Err(miette!("empty repository path"));
     }
 
     if path.contains("..") {
-        eyre::bail!("invalid repository path: {raw}");
+        return Err(miette!("invalid repository path: {raw}"));
     }
 
     if !path.ends_with(".git") {
-        eyre::bail!("invalid repository path (must end in .git): {raw}");
+        return Err(miette!("invalid repository path (must end in .git): {raw}"));
     }
 
     if path.contains("//") {
-        eyre::bail!("invalid repository path: {raw}");
+        return Err(miette!("invalid repository path: {raw}"));
     }
 
     Ok(path.to_string())
index 1d09b84eebfed87b0b501742383fb6871f7e8458..114afed6c6ba9399661ff5cd8110208c139f2282 100644 (file)
@@ -1,4 +1,6 @@
-pub async fn run() -> color_eyre::Result<()> {
+use miette::Result;
+
+pub async fn run() -> Result<()> {
     tracing::info!("quire serve starting");
     // TODO: bind HTTP server
     Ok(())
index 0c96e5e5cf08cf4bbaedfe24a610052e561a7532..f9d56fc83c1c1d2e5960c4c86f2aca22bf173a7a 100644 (file)
@@ -2,6 +2,8 @@ mod commands;
 
 use clap::{CommandFactory, Parser, Subcommand};
 use clap_complete::Shell;
+use miette::IntoDiagnostic;
+use miette::Result;
 use tracing_subscriber::EnvFilter;
 use tracing_subscriber::fmt;
 use tracing_subscriber::prelude::*;
@@ -37,8 +39,7 @@ enum Commands {
 }
 
 #[tokio::main]
-async fn main() -> color_eyre::Result<()> {
-    color_eyre::install()?;
+async fn main() -> Result<()> {
     tracing_subscriber::registry()
         .with(fmt::layer())
         .with(EnvFilter::from_default_env())
@@ -52,7 +53,7 @@ async fn main() -> color_eyre::Result<()> {
     }
 
     let Some(command) = cli.command else {
-        Cli::command().print_help()?;
+        Cli::command().print_help().into_diagnostic()?;
         return Ok(());
     };
 
index 577c6937ae9ee748eee25fd8c479707e84ec1590..de0493db44827b434c96cb9cdc7db19b0931e0fe 100644 (file)
@@ -1,4 +1,4 @@
-#[derive(Debug, thiserror::Error)]
+#[derive(Debug, thiserror::Error, miette::Diagnostic)]
 pub enum Error {
     #[error("not found: {0}")]
     NotFound(String),