Skip to content

refactor(BREAKING): remove anyhow#173

Open
dsherret wants to merge 13 commits into
denoland:mainfrom
dsherret:remove_anyhow
Open

refactor(BREAKING): remove anyhow#173
dsherret wants to merge 13 commits into
denoland:mainfrom
dsherret:remove_anyhow

Conversation

@dsherret
Copy link
Copy Markdown
Contributor

@dsherret dsherret commented Apr 22, 2026

Waiting on #171

Breaking changes (AI summary):

1. parser::parse — return type changed
  // before
  pub fn parse(input: &str) -> Result<SequentialList>            // anyhow::Result, i.e. Err = anyhow::Error
  // after
  pub fn parse(input: &str) -> Result<SequentialList, monch::ParseErrorFailureError>
  This is a breaking change for any caller that named the error type or relied on anyhow::Error's API (.downcast(),
  .context(), etc.). It also leaks monch::ParseErrorFailureError into your public surface.

  2. ShellPipeWriter / ShellPipeReader methods — error type narrowed
  pipe_to(...)        -> Result<()>     →  std::io::Result<()>
  pipe_to_sender(...) -> Result<()>     →  std::io::Result<()>
  read(...)           -> Result<usize>  →  std::io::Result<usize>
  write_all(...)      -> Result<()>     →  std::io::Result<()>
  write_line(...)     -> Result<()>     →  std::io::Result<()>
  Narrowing anyhow::Error → std::io::Error is breaking in signature, though arguably an improvement (more precise).

Closes #172

dsherret added 11 commits April 21, 2026 20:48
Replace the opaque `is_async` boolean with two orthogonal flags —
`is_async` (was `&`) and `ends_line` (followed by `\n` / `\r\n`) — so
consumers can apply different semantics per boundary. In particular,
`cmd &\nnext` is now represented as `is_async: true, ends_line: true`
instead of losing the newline.
Abort a sequential list at the first non-zero command when the errexit
option is enabled. Failure inside `&&` / `||` chains or pipelines is
unaffected, matching bash semantics.
@dsherret dsherret changed the title refactor: remove anyhow refactor(BREAKING): remove anyhow May 20, 2026
@dsherret dsherret marked this pull request as ready for review May 20, 2026 13:50
@dsherret dsherret changed the title refactor(BREAKING): remove anyhow refactor: remove anyhow May 20, 2026
@dsherret dsherret changed the title refactor: remove anyhow refactor(BREAKING): remove anyhow May 20, 2026
Copy link
Copy Markdown
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactor — anyhow is fully gone, all platforms green, and narrowing the ShellPipeReader/Writer signatures to std::io::Result is a nice improvement. A couple of design-level notes inline:

  • The biggest thing I'd push back on is the monch::ParseErrorFailureError leak into the public signature of parse(). You note it in the PR body, but worth deciding whether to re-export the type from the crate root or wrap it in a crate-local newtype — otherwise every downstream caller that wants to name the error type has to add monch as a direct dep.
  • The From<io::Error> (and friends) impls on ShellCommandError stringify via to_string(), which drops the source chain. Probably fine given the type's documented "lightweight stderr reporting" use case, but flagging because it's a real behavior change vs. anyhow.

Nothing blocking from me, leaving as a comment review.

Comment thread src/parser.rs
}

pub fn parse(input: &str) -> Result<SequentialList> {
pub fn parse(input: &str) -> Result<SequentialList, ParseErrorFailureError> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

monch::ParseErrorFailureError is now part of the public signature of parse(), but monch isn't re-exported from the crate root. Downstream callers that want to name this error type (e.g. to map it into their own error enum) will need to add monch as a direct dependency, which is a bit awkward. Two options:

  1. pub use monch::ParseErrorFailureError; at the crate root (cheap, but pins monch in the public API).
  2. Wrap in a crate-local newtype like pub struct ParseError(ParseErrorFailureError); and return that instead — keeps monch as an implementation detail.

The PR body already calls this out; just want to make sure it's a deliberate choice before this merges.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by re-exporting monch's error.


impl From<std::io::Error> for ShellCommandError {
fn from(err: std::io::Error) -> Self {
Self(err.to_string())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These From impls stringify via to_string(), which means the source chain is lost — a caller can't, say, downcast to io::Error to inspect ErrorKind::NotFound. Given the type's doc comment ("lightweight 'print the message via stderr' reporting") that's probably intentional, but it's a real behavioral difference vs. the old anyhow path which preserved the chain.

If you want both the simple-message ergonomics and a preserved source, a thiserror enum would do it:

#[derive(Debug, thiserror::Error)]
pub enum ShellCommandError {
  #[error("{0}")] Message(String),
  #[error(transparent)] Io(#[from] std::io::Error),
  #[error(transparent)] Utf8(#[from] FromUtf8Error),
  // ...
}

Not blocking — just want to make sure the trade-off is intentional.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current is fine because these all just get written as a string.

&& child.id().is_some()
{
panic!("Could not track process: {:#}", err);
panic!("Could not track process: {}", err);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: these used to be {:#} (anyhow's alternate Debug, which prints the cause chain). Now plain {} (Display only). With the current types the underlying error is io::Error so nothing's lost, but if a future change wraps a richer error here, the panic message would silently drop the inner cause. Worth keeping in mind.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably ok. This is only a debug assertion too.

@dsherret dsherret requested a review from bartlomieju May 21, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove use of anyhow in this library

2 participants