Replace anyhow with color-eyre and tracing-subscriber
color-eyre gives colored error reports with span traces. tracing-subscriber
enables structured logging via RUST_LOG. Introduced InvalidStateError so
State::FromStr returns a proper error type and ? works without map_err.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/Cargo.lock b/Cargo.lock
index cca6353..a928f35 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,30 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "addr2line"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "allocator-api2"
version = "0.2.21"
@@ -94,6 +118,21 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+[[package]]
+name = "backtrace"
+version = "0.3.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-link",
+]
+
[[package]]
name = "base64"
version = "0.22.1"
@@ -203,6 +242,33 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
+[[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.4"
@@ -366,6 +432,16 @@ dependencies = [
"pin-project-lite",
]
+[[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.3.0"
@@ -509,6 +585,12 @@ dependencies = [
"wasip3",
]
+[[package]]
+name = "gimli"
+version = "0.32.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
+
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -682,6 +764,12 @@ dependencies = [
"icu_properties",
]
+[[package]]
+name = "indenter"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
+
[[package]]
name = "indexmap"
version = "2.13.0"
@@ -824,6 +912,15 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
[[package]]
name = "md-5"
version = "0.10.6"
@@ -840,6 +937,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
[[package]]
name = "mio"
version = "1.1.1"
@@ -851,6 +957,15 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
@@ -897,6 +1012,15 @@ dependencies = [
"libm",
]
+[[package]]
+name = "object"
+version = "0.37.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -909,6 +1033,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+[[package]]
+name = "owo-colors"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
+
[[package]]
name = "parking"
version = "2.2.1"
@@ -1120,9 +1250,9 @@ dependencies = [
name = "ranger"
version = "0.1.0"
dependencies = [
- "anyhow",
"assert_cmd",
"clap",
+ "color-eyre",
"jiff",
"rand",
"serde",
@@ -1131,6 +1261,7 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
+ "tracing-subscriber",
"xdg",
]
@@ -1157,6 +1288,17 @@ name = "regex-automata"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rsa"
@@ -1178,6 +1320,12 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "rustc-demangle"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
+
[[package]]
name = "rustix"
version = "1.1.4"
@@ -1286,6 +1434,15 @@ dependencies = [
"digest",
]
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -1634,6 +1791,15 @@ dependencies = [
"syn",
]
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "tinystr"
version = "0.8.2"
@@ -1728,6 +1894,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
+ "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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
]
[[package]]
@@ -1793,6 +1999,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
diff --git a/Cargo.toml b/Cargo.toml
index ee468c6..61ebb6a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,8 @@ name = "ranger"
path = "src/bin/ranger/main.rs"
[dependencies]
-anyhow = "*"
+color-eyre = "*"
+tracing-subscriber = { version = "*", features = ["env-filter"] }
jiff = { version = "*", features = ["serde"] }
clap = { version = "*", features = ["derive", "env"] }
rand = "*"
diff --git a/src/bin/ranger/commands/backlog.rs b/src/bin/ranger/commands/backlog.rs
index 668f1f3..96a9dfa 100644
--- a/src/bin/ranger/commands/backlog.rs
+++ b/src/bin/ranger/commands/backlog.rs
@@ -1,4 +1,5 @@
use clap::Subcommand;
+use color_eyre::eyre::Result;
use ranger::db::SqlitePool;
use ranger::models::Backlog;
use ranger::ops;
@@ -21,7 +22,7 @@ pub enum BacklogCommands {
},
}
-pub async fn run(pool: &SqlitePool, command: BacklogCommands, json: bool) -> anyhow::Result<()> {
+pub async fn run(pool: &SqlitePool, command: BacklogCommands, json: bool) -> Result<()> {
match command {
BacklogCommands::Create { name } => {
let backlog = ops::backlog::create(pool, &name).await?;
diff --git a/src/bin/ranger/commands/blocker.rs b/src/bin/ranger/commands/blocker.rs
index 921e9d3..9a126c8 100644
--- a/src/bin/ranger/commands/blocker.rs
+++ b/src/bin/ranger/commands/blocker.rs
@@ -1,4 +1,5 @@
use clap::Subcommand;
+use color_eyre::eyre::Result;
use ranger::db::SqlitePool;
use ranger::ops;
@@ -22,7 +23,7 @@ pub enum BlockerCommands {
},
}
-pub async fn run(pool: &SqlitePool, command: BlockerCommands, json: bool) -> anyhow::Result<()> {
+pub async fn run(pool: &SqlitePool, command: BlockerCommands, json: bool) -> Result<()> {
match command {
BlockerCommands::Add { task, blocked_by } => {
let t = ops::task::get_by_key_prefix(pool, &task).await?;
diff --git a/src/bin/ranger/commands/comment.rs b/src/bin/ranger/commands/comment.rs
index 95b2cff..d304ce1 100644
--- a/src/bin/ranger/commands/comment.rs
+++ b/src/bin/ranger/commands/comment.rs
@@ -1,4 +1,5 @@
use clap::Subcommand;
+use color_eyre::eyre::Result;
use ranger::db::SqlitePool;
use ranger::ops;
@@ -20,7 +21,7 @@ pub enum CommentCommands {
},
}
-pub async fn run(pool: &SqlitePool, command: CommentCommands, json: bool) -> anyhow::Result<()> {
+pub async fn run(pool: &SqlitePool, command: CommentCommands, json: bool) -> Result<()> {
match command {
CommentCommands::Add { task, body } => {
let t = ops::task::get_by_key_prefix(pool, &task).await?;
diff --git a/src/bin/ranger/commands/tag.rs b/src/bin/ranger/commands/tag.rs
index 841956f..4ff0487 100644
--- a/src/bin/ranger/commands/tag.rs
+++ b/src/bin/ranger/commands/tag.rs
@@ -1,4 +1,5 @@
use clap::Subcommand;
+use color_eyre::eyre::Result;
use ranger::db::SqlitePool;
use ranger::ops;
@@ -10,7 +11,7 @@ pub enum TagCommands {
List,
}
-pub async fn run(pool: &SqlitePool, command: TagCommands, json: bool) -> anyhow::Result<()> {
+pub async fn run(pool: &SqlitePool, command: TagCommands, json: bool) -> Result<()> {
match command {
TagCommands::List => {
let tags = ops::tag::list(pool).await?;
diff --git a/src/bin/ranger/commands/task.rs b/src/bin/ranger/commands/task.rs
index d7fa8a6..4ee5aee 100644
--- a/src/bin/ranger/commands/task.rs
+++ b/src/bin/ranger/commands/task.rs
@@ -1,4 +1,5 @@
use clap::Subcommand;
+use color_eyre::eyre::Result;
use ranger::db::SqlitePool;
use ranger::models::{State, Task};
use ranger::ops;
@@ -96,7 +97,7 @@ pub enum TaskCommands {
},
}
-pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> anyhow::Result<()> {
+pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> Result<()> {
match command {
TaskCommands::Create {
title,
@@ -126,10 +127,7 @@ pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> anyhow
None
};
- let state = state
- .map(|s| s.parse::<State>())
- .transpose()
- .map_err(|e| anyhow::anyhow!(e))?;
+ let state = state.map(|s| s.parse::<State>()).transpose()?;
let task = ops::task::create(
pool,
@@ -155,10 +153,7 @@ pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> anyhow
output::print(&task, json, print_task);
}
TaskCommands::List { backlog, state } => {
- let state = state
- .map(|s| s.parse::<State>())
- .transpose()
- .map_err(|e| anyhow::anyhow!(e))?;
+ let state = state.map(|s| s.parse::<State>()).transpose()?;
if let Some(backlog_key) = &backlog {
let bl = ops::backlog::get_by_key_prefix(pool, backlog_key).await?;
@@ -222,10 +217,7 @@ pub async fn run(pool: &SqlitePool, command: TaskCommands, json: bool) -> anyhow
description,
state,
} => {
- let state = state
- .map(|s| s.parse::<State>())
- .transpose()
- .map_err(|e| anyhow::anyhow!(e))?;
+ let state = state.map(|s| s.parse::<State>()).transpose()?;
let task = ops::task::get_by_key_prefix(pool, &key).await?;
let updated = ops::task::edit(
diff --git a/src/bin/ranger/main.rs b/src/bin/ranger/main.rs
index eb40d2c..8e11172 100644
--- a/src/bin/ranger/main.rs
+++ b/src/bin/ranger/main.rs
@@ -58,7 +58,12 @@ fn resolve_db_path(cli_path: Option<PathBuf>) -> PathBuf {
}
#[tokio::main]
-async fn main() -> anyhow::Result<()> {
+async fn main() -> color_eyre::Result<()> {
+ color_eyre::install()?;
+ tracing_subscriber::fmt()
+ .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+ .init();
+
let cli = Cli::parse();
let db_path = resolve_db_path(cli.db);
let pool = ranger::db::connect(&db_path).await?;
diff --git a/src/models.rs b/src/models.rs
index dc9d992..e81a22f 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -23,15 +23,19 @@ impl State {
}
}
+#[derive(Debug, thiserror::Error)]
+#[error("invalid state: '{0}'")]
+pub struct InvalidStateError(String);
+
impl std::str::FromStr for State {
- type Err = String;
+ type Err = InvalidStateError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"icebox" => Ok(State::Icebox),
"queued" => Ok(State::Queued),
"in_progress" => Ok(State::InProgress),
"done" => Ok(State::Done),
- _ => Err(format!("invalid state: {s}")),
+ _ => Err(InvalidStateError(s.to_string())),
}
}
}