Skip to content

Commit 158d8a0

Browse files
committed
Port to anyhow/thiserror
The `failure` crate isn't being actively developed anymore; having it as a dependency in the public API is a problem for consumers that don't use it. Instead, use anyhow/thiserror; in particular the public API for errors now implements `std::error::Error`, which allows *consuming* applications to e.g. directly use `?` rather than needing a `.map_err(|e| e.compat())`. As the docs for `thiserror` say: > Thiserror deliberately does not appear in your public API. You get > the same thing as if you had written an implementation of > std::error::Error by hand, and switching from handwritten impls > to thiserror or vice versa is not a breaking change. And we're only using `anyhow` internally.
1 parent 236482a commit 158d8a0

File tree

2 files changed

+25
-17
lines changed

2 files changed

+25
-17
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ repository = "http://github.com/tcr/commandspec"
77
version = "0.12.2"
88

99
[dependencies]
10-
failure = "0.1.1"
10+
thiserror = "1.0"
11+
anyhow = "1.0"
1112
shlex = "0.1.1"
1213
lazy_static = "1.0.0"
1314
log = "0.4.4"

src/lib.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
extern crate shlex;
22
#[macro_use]
3-
extern crate failure;
3+
extern crate thiserror;
4+
#[macro_use]
5+
extern crate anyhow;
46
#[macro_use]
57
extern crate lazy_static;
68
#[macro_use]
@@ -21,8 +23,7 @@ use std::sync::Mutex;
2123
use std::path::{Path, PathBuf};
2224
use std::process::Stdio;
2325

24-
// Re-export for macros.
25-
pub use failure::Error;
26+
use thiserror::Error;
2627

2728
pub mod macros;
2829
mod process;
@@ -79,15 +80,15 @@ pub trait CommandSpecExt {
7980
fn scoped_spawn(self) -> Result<SpawnGuard, ::std::io::Error>;
8081
}
8182

82-
#[derive(Debug, Fail)]
83+
#[derive(Debug, Error)]
8384
pub enum CommandError {
84-
#[fail(display = "Encountered an IO error: {:?}", _0)]
85-
Io(#[cause] ::std::io::Error),
85+
#[error("Encountered an IO error: {0:?}")]
86+
Io(#[from] ::std::io::Error),
8687

87-
#[fail(display = "Command was interrupted.")]
88+
#[error("Command was interrupted")]
8889
Interrupt,
8990

90-
#[fail(display = "Command failed with error code {}.", _0)]
91+
#[error("Command failed with error code {0}")]
9192
Code(i32),
9293
}
9394

@@ -313,14 +314,14 @@ where P: Into<&'p Path> {
313314
}
314315

315316
#[cfg(not(windows))]
316-
fn canonicalize_path<'p, P>(path: P) -> Result<PathBuf, Error>
317+
fn canonicalize_path<'p, P>(path: P) -> Result<PathBuf, Box<std::error::Error>>
317318
where P: Into<&'p Path> {
318319
Ok(path.into().canonicalize()?)
319320
}
320321

321322
//---------------
322323

323-
pub fn commandify(value: String) -> Result<Command, Error> {
324+
pub fn commandify(value: String) -> Result<Command, Box<dyn std::error::Error>> {
324325
let lines = value.trim().split("\n").map(String::from).collect::<Vec<_>>();
325326

326327
#[derive(Debug, PartialEq)]
@@ -347,20 +348,26 @@ pub fn commandify(value: String) -> Result<Command, Error> {
347348
match line.get(0).map(|x| x.as_ref()) {
348349
Some("cd") => {
349350
if state != SpecState::Cd {
350-
bail!("cd should be the first line in your command! macro.");
351+
Err("cd should be the first line in your command! macro.")?;
352+
}
353+
if line.len() != 2 {
354+
Err(format!("Too many arguments in cd; expected 1, found {}", line.len() - 1))?;
351355
}
352-
ensure!(line.len() == 2, "Too many arguments in cd; expected 1, found {}", line.len() - 1);
353356
cd = Some(line.remove(1));
354357
state = SpecState::Env;
355358
}
356359
Some("export") => {
357360
if state != SpecState::Cd && state != SpecState::Env {
358-
bail!("exports should follow cd but precede your command in the command! macro.");
361+
Err("exports should follow cd but precede your command in the command! macro.")?;
362+
}
363+
if line.len() >= 2 {
364+
Err(format!("Not enough arguments in export; expected at least 1, found {}", line.len() - 1))?;
359365
}
360-
ensure!(line.len() >= 2, "Not enough arguments in export; expected at least 1, found {}", line.len() - 1);
361366
for item in &line[1..] {
362367
let mut items = item.splitn(2, "=").collect::<Vec<_>>();
363-
ensure!(items.len() > 0, "Expected export of the format NAME=VALUE");
368+
if items.len() > 0 {
369+
Err("Expected export of the format NAME=VALUE")?;
370+
}
364371
env.insert(items[0].to_string(), items[1].to_string());
365372
}
366373
state = SpecState::Env;
@@ -373,7 +380,7 @@ pub fn commandify(value: String) -> Result<Command, Error> {
373380
}
374381
}
375382
if state != SpecState::Cmd || command_lines.is_empty() {
376-
bail!("Didn't find a command in your command! macro.");
383+
Err("Didn't find a command in your command! macro.")?;
377384
}
378385

379386
// Join the command string and split out binary / args.

0 commit comments

Comments
 (0)