core: switch to anyhow

This commit adds `anyhow` as a dependency and switches over to it from
Box<dyn Error>.

It actually looks like I've kept all of my errors rather shallow, such
that we don't get a huge benefit from anyhow at present. But now that
anyhow is in use, I expect to use its "context" feature more going
forward.
This commit is contained in:
Andrew Gallant 2023-10-12 12:16:42 -04:00
parent 53679e4c43
commit 92c81b1225
6 changed files with 105 additions and 104 deletions

7
Cargo.lock generated
View File

@ -11,6 +11,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -426,6 +432,7 @@ checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33"
name = "ripgrep" name = "ripgrep"
version = "13.0.0" version = "13.0.0"
dependencies = [ dependencies = [
"anyhow",
"bstr", "bstr",
"clap", "clap",
"grep", "grep",

View File

@ -49,6 +49,7 @@ members = [
] ]
[dependencies] [dependencies]
anyhow = "1.0.75"
bstr = "1.6.0" bstr = "1.6.0"
grep = { version = "0.2.12", path = "crates/grep" } grep = { version = "0.2.12", path = "crates/grep" }
ignore = { version = "0.4.19", path = "crates/ignore" } ignore = { version = "0.4.19", path = "crates/ignore" }

View File

@ -46,7 +46,6 @@ use crate::{
messages::{set_ignore_messages, set_messages}, messages::{set_ignore_messages, set_messages},
search::{PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder}, search::{PatternMatcher, Printer, SearchWorker, SearchWorkerBuilder},
subject::{Subject, SubjectBuilder}, subject::{Subject, SubjectBuilder},
Result,
}; };
/// The command that ripgrep should execute based on the command line /// The command that ripgrep should execute based on the command line
@ -129,7 +128,7 @@ impl Args {
/// ripgrep, then print the version and exit. /// ripgrep, then print the version and exit.
/// ///
/// Also, initialize a global logger. /// Also, initialize a global logger.
pub fn parse() -> Result<Args> { pub fn parse() -> anyhow::Result<Args> {
// We parse the args given on CLI. This does not include args from // We parse the args given on CLI. This does not include args from
// the config. We use the CLI args as an initial configuration while // the config. We use the CLI args as an initial configuration while
// trying to parse config files. If a config file exists and has // trying to parse config files. If a config file exists and has
@ -140,7 +139,7 @@ impl Args {
set_ignore_messages(!early_matches.is_present("no-ignore-messages")); set_ignore_messages(!early_matches.is_present("no-ignore-messages"));
if let Err(err) = Logger::init() { if let Err(err) = Logger::init() {
return Err(format!("failed to initialize logger: {}", err).into()); anyhow::bail!("failed to initialize logger: {err}");
} }
if early_matches.is_present("trace") { if early_matches.is_present("trace") {
log::set_max_level(log::LevelFilter::Trace); log::set_max_level(log::LevelFilter::Trace);
@ -194,7 +193,7 @@ impl Args {
/// search results. /// search results.
/// ///
/// The returned printer will write results to the given writer. /// The returned printer will write results to the given writer.
fn printer<W: WriteColor>(&self, wtr: W) -> Result<Printer<W>> { fn printer<W: WriteColor>(&self, wtr: W) -> anyhow::Result<Printer<W>> {
match self.matches().output_kind() { match self.matches().output_kind() {
OutputKind::Standard => { OutputKind::Standard => {
let separator_search = self.command() == Command::Search; let separator_search = self.command() == Command::Search;
@ -218,7 +217,7 @@ impl Args {
impl Args { impl Args {
/// Create a new buffer writer for multi-threaded printing with color /// Create a new buffer writer for multi-threaded printing with color
/// support. /// support.
pub fn buffer_writer(&self) -> Result<BufferWriter> { pub fn buffer_writer(&self) -> anyhow::Result<BufferWriter> {
let mut wtr = BufferWriter::stdout(self.matches().color_choice()); let mut wtr = BufferWriter::stdout(self.matches().color_choice());
wtr.separator(self.matches().file_separator()?); wtr.separator(self.matches().file_separator()?);
Ok(wtr) Ok(wtr)
@ -236,7 +235,7 @@ impl Args {
pub fn path_printer<W: WriteColor>( pub fn path_printer<W: WriteColor>(
&self, &self,
wtr: W, wtr: W,
) -> Result<PathPrinter<W>> { ) -> anyhow::Result<PathPrinter<W>> {
let mut builder = PathPrinterBuilder::new(); let mut builder = PathPrinterBuilder::new();
builder builder
.color_specs(self.matches().color_specs()?) .color_specs(self.matches().color_specs()?)
@ -253,7 +252,7 @@ impl Args {
/// Returns true if and only if the search should quit after finding the /// Returns true if and only if the search should quit after finding the
/// first match. /// first match.
pub fn quit_after_match(&self) -> Result<bool> { pub fn quit_after_match(&self) -> anyhow::Result<bool> {
Ok(self.matches().is_present("quiet") && self.stats()?.is_none()) Ok(self.matches().is_present("quiet") && self.stats()?.is_none())
} }
@ -263,7 +262,7 @@ impl Args {
pub fn search_worker<W: WriteColor>( pub fn search_worker<W: WriteColor>(
&self, &self,
wtr: W, wtr: W,
) -> Result<SearchWorker<W>> { ) -> anyhow::Result<SearchWorker<W>> {
let matches = self.matches(); let matches = self.matches();
let matcher = self.matcher().clone(); let matcher = self.matcher().clone();
let printer = self.printer(wtr)?; let printer = self.printer(wtr)?;
@ -284,7 +283,7 @@ impl Args {
/// ///
/// When this returns a `Stats` value, then it is guaranteed that the /// When this returns a `Stats` value, then it is guaranteed that the
/// search worker will be configured to track statistics as well. /// search worker will be configured to track statistics as well.
pub fn stats(&self) -> Result<Option<Stats>> { pub fn stats(&self) -> anyhow::Result<Option<Stats>> {
Ok(if self.command().is_search() && self.matches().stats() { Ok(if self.command().is_search() && self.matches().stats() {
Some(Stats::new()) Some(Stats::new())
} else { } else {
@ -318,12 +317,12 @@ impl Args {
/// ///
/// If there was a problem reading and parsing the type definitions, then /// If there was a problem reading and parsing the type definitions, then
/// this returns an error. /// this returns an error.
pub fn type_defs(&self) -> Result<Vec<FileTypeDef>> { pub fn type_defs(&self) -> anyhow::Result<Vec<FileTypeDef>> {
Ok(self.matches().types()?.definitions().to_vec()) Ok(self.matches().types()?.definitions().to_vec())
} }
/// Return a walker that never uses additional threads. /// Return a walker that never uses additional threads.
pub fn walker(&self) -> Result<Walk> { pub fn walker(&self) -> anyhow::Result<Walk> {
Ok(self Ok(self
.matches() .matches()
.walker_builder(self.paths(), self.0.threads)? .walker_builder(self.paths(), self.0.threads)?
@ -371,7 +370,7 @@ impl Args {
} }
/// Return a parallel walker that may use additional threads. /// Return a parallel walker that may use additional threads.
pub fn walker_parallel(&self) -> Result<WalkParallel> { pub fn walker_parallel(&self) -> anyhow::Result<WalkParallel> {
Ok(self Ok(self
.matches() .matches()
.walker_builder(self.paths(), self.0.threads)? .walker_builder(self.paths(), self.0.threads)?
@ -434,7 +433,7 @@ impl SortBy {
/// Try to check that the sorting criteria selected is actually supported. /// Try to check that the sorting criteria selected is actually supported.
/// If it isn't, then an error is returned. /// If it isn't, then an error is returned.
fn check(&self) -> Result<()> { fn check(&self) -> anyhow::Result<()> {
match self.kind { match self.kind {
SortByKind::None | SortByKind::Path => {} SortByKind::None | SortByKind::Path => {}
SortByKind::LastModified => { SortByKind::LastModified => {
@ -509,7 +508,7 @@ impl ArgMatches {
/// ///
/// If there are no additional arguments from the environment (e.g., a /// If there are no additional arguments from the environment (e.g., a
/// config file), then the given matches are returned as is. /// config file), then the given matches are returned as is.
fn reconfigure(self) -> Result<ArgMatches> { fn reconfigure(self) -> anyhow::Result<ArgMatches> {
// If the end user says no config, then respect it. // If the end user says no config, then respect it.
if self.is_present("no-config") { if self.is_present("no-config") {
log::debug!( log::debug!(
@ -534,7 +533,7 @@ impl ArgMatches {
/// Convert the result of parsing CLI arguments into ripgrep's higher level /// Convert the result of parsing CLI arguments into ripgrep's higher level
/// configuration structure. /// configuration structure.
fn to_args(self) -> Result<Args> { fn to_args(self) -> anyhow::Result<Args> {
// We compute these once since they could be large. // We compute these once since they could be large.
let patterns = self.patterns()?; let patterns = self.patterns()?;
let matcher = self.matcher(&patterns)?; let matcher = self.matcher(&patterns)?;
@ -591,7 +590,7 @@ impl ArgMatches {
/// ///
/// If there was a problem building the matcher (e.g., a syntax error), /// If there was a problem building the matcher (e.g., a syntax error),
/// then this returns an error. /// then this returns an error.
fn matcher(&self, patterns: &[String]) -> Result<PatternMatcher> { fn matcher(&self, patterns: &[String]) -> anyhow::Result<PatternMatcher> {
if self.is_present("pcre2") { if self.is_present("pcre2") {
self.matcher_engine("pcre2", patterns) self.matcher_engine("pcre2", patterns)
} else if self.is_present("auto-hybrid-regex") { } else if self.is_present("auto-hybrid-regex") {
@ -611,13 +610,13 @@ impl ArgMatches {
&self, &self,
engine: &str, engine: &str,
patterns: &[String], patterns: &[String],
) -> Result<PatternMatcher> { ) -> anyhow::Result<PatternMatcher> {
match engine { match engine {
"default" => { "default" => {
let matcher = match self.matcher_rust(patterns) { let matcher = match self.matcher_rust(patterns) {
Ok(matcher) => matcher, Ok(matcher) => matcher,
Err(err) => { Err(err) => {
return Err(From::from(suggest(err.to_string()))); anyhow::bail!(suggest(err.to_string()));
} }
}; };
Ok(PatternMatcher::RustRegex(matcher)) Ok(PatternMatcher::RustRegex(matcher))
@ -628,9 +627,9 @@ impl ArgMatches {
Ok(PatternMatcher::PCRE2(matcher)) Ok(PatternMatcher::PCRE2(matcher))
} }
#[cfg(not(feature = "pcre2"))] #[cfg(not(feature = "pcre2"))]
"pcre2" => Err(From::from( "pcre2" => anyhow::bail!(
"PCRE2 is not available in this build of ripgrep", "PCRE2 is not available in this build of ripgrep",
)), ),
"auto" => { "auto" => {
let rust_err = match self.matcher_rust(patterns) { let rust_err = match self.matcher_rust(patterns) {
Ok(matcher) => { Ok(matcher) => {
@ -647,21 +646,18 @@ impl ArgMatches {
Ok(matcher) => return Ok(matcher), Ok(matcher) => return Ok(matcher),
Err(err) => err, Err(err) => err,
}; };
Err(From::from(format!( let divider = "~".repeat(79);
anyhow::bail!(
"regex could not be compiled with either the default \ "regex could not be compiled with either the default \
regex engine or with PCRE2.\n\n\ regex engine or with PCRE2.\n\n\
default regex engine error:\n{}\n{}\n{}\n\n\ default regex engine error:\n\
PCRE2 regex engine error:\n{}", {divider}\n\
"~".repeat(79), {rust_err}\n\
rust_err, {divider}\n\n\
"~".repeat(79), PCRE2 regex engine error:\n{pcre_err}",
pcre_err, );
)))
} }
_ => Err(From::from(format!( _ => anyhow::bail!("unrecognized regex engine '{engine}'"),
"unrecognized regex engine '{}'",
engine
))),
} }
} }
@ -669,7 +665,10 @@ impl ArgMatches {
/// ///
/// If there was a problem building the matcher (such as a regex syntax /// If there was a problem building the matcher (such as a regex syntax
/// error), then an error is returned. /// error), then an error is returned.
fn matcher_rust(&self, patterns: &[String]) -> Result<RustRegexMatcher> { fn matcher_rust(
&self,
patterns: &[String],
) -> anyhow::Result<RustRegexMatcher> {
let mut builder = RustRegexMatcherBuilder::new(); let mut builder = RustRegexMatcherBuilder::new();
builder builder
.case_smart(self.case_smart()) .case_smart(self.case_smart())
@ -707,7 +706,7 @@ impl ArgMatches {
} }
match builder.build_many(patterns) { match builder.build_many(patterns) {
Ok(m) => Ok(m), Ok(m) => Ok(m),
Err(err) => Err(From::from(suggest_multiline(err.to_string()))), Err(err) => anyhow::bail!(suggest_multiline(err.to_string())),
} }
} }
@ -716,7 +715,10 @@ impl ArgMatches {
/// If there was a problem building the matcher (such as a regex syntax /// If there was a problem building the matcher (such as a regex syntax
/// error), then an error is returned. /// error), then an error is returned.
#[cfg(feature = "pcre2")] #[cfg(feature = "pcre2")]
fn matcher_pcre2(&self, patterns: &[String]) -> Result<PCRE2RegexMatcher> { fn matcher_pcre2(
&self,
patterns: &[String],
) -> anyhow::Result<PCRE2RegexMatcher> {
let mut builder = PCRE2RegexMatcherBuilder::new(); let mut builder = PCRE2RegexMatcherBuilder::new();
builder builder
.case_smart(self.case_smart()) .case_smart(self.case_smart())
@ -748,7 +750,7 @@ impl ArgMatches {
} }
/// Build a JSON printer that writes results to the given writer. /// Build a JSON printer that writes results to the given writer.
fn printer_json<W: io::Write>(&self, wtr: W) -> Result<JSON<W>> { fn printer_json<W: io::Write>(&self, wtr: W) -> anyhow::Result<JSON<W>> {
let mut builder = JSONBuilder::new(); let mut builder = JSONBuilder::new();
builder builder
.pretty(false) .pretty(false)
@ -774,7 +776,7 @@ impl ArgMatches {
paths: &[PathBuf], paths: &[PathBuf],
wtr: W, wtr: W,
separator_search: bool, separator_search: bool,
) -> Result<Standard<W>> { ) -> anyhow::Result<Standard<W>> {
let mut builder = StandardBuilder::new(); let mut builder = StandardBuilder::new();
builder builder
.color_specs(self.color_specs()?) .color_specs(self.color_specs()?)
@ -813,7 +815,7 @@ impl ArgMatches {
&self, &self,
paths: &[PathBuf], paths: &[PathBuf],
wtr: W, wtr: W,
) -> Result<Summary<W>> { ) -> anyhow::Result<Summary<W>> {
let mut builder = SummaryBuilder::new(); let mut builder = SummaryBuilder::new();
builder builder
.kind(self.summary_kind().expect("summary format")) .kind(self.summary_kind().expect("summary format"))
@ -830,7 +832,7 @@ impl ArgMatches {
} }
/// Build a searcher from the command line parameters. /// Build a searcher from the command line parameters.
fn searcher(&self, paths: &[PathBuf]) -> Result<Searcher> { fn searcher(&self, paths: &[PathBuf]) -> anyhow::Result<Searcher> {
let (ctx_before, ctx_after) = self.contexts()?; let (ctx_before, ctx_after) = self.contexts()?;
let line_term = if self.is_present("crlf") { let line_term = if self.is_present("crlf") {
LineTerminator::crlf() LineTerminator::crlf()
@ -871,7 +873,7 @@ impl ArgMatches {
&self, &self,
paths: &[PathBuf], paths: &[PathBuf],
threads: usize, threads: usize,
) -> Result<WalkBuilder> { ) -> anyhow::Result<WalkBuilder> {
let mut builder = WalkBuilder::new(&paths[0]); let mut builder = WalkBuilder::new(&paths[0]);
for path in &paths[1..] { for path in &paths[1..] {
builder.add(path); builder.add(path);
@ -994,7 +996,7 @@ impl ArgMatches {
/// ///
/// If the was a problem parsing any of the provided specs, then an error /// If the was a problem parsing any of the provided specs, then an error
/// is returned. /// is returned.
fn color_specs(&self) -> Result<ColorSpecs> { fn color_specs(&self) -> anyhow::Result<ColorSpecs> {
// Start with a default set of color specs. // Start with a default set of color specs.
let mut specs = default_color_specs(); let mut specs = default_color_specs();
for spec_str in self.values_of_lossy_vec("colors") { for spec_str in self.values_of_lossy_vec("colors") {
@ -1017,7 +1019,7 @@ impl ArgMatches {
/// ///
/// If there was a problem parsing the values from the user as an integer, /// If there was a problem parsing the values from the user as an integer,
/// then an error is returned. /// then an error is returned.
fn contexts(&self) -> Result<(usize, usize)> { fn contexts(&self) -> anyhow::Result<(usize, usize)> {
let both = self.usize_of("context")?.unwrap_or(0); let both = self.usize_of("context")?.unwrap_or(0);
let after = self.usize_of("after-context")?.unwrap_or(both); let after = self.usize_of("after-context")?.unwrap_or(both);
let before = self.usize_of("before-context")?.unwrap_or(both); let before = self.usize_of("before-context")?.unwrap_or(both);
@ -1061,7 +1063,7 @@ impl ArgMatches {
} }
/// Parse the dfa-size-limit argument option into a byte count. /// Parse the dfa-size-limit argument option into a byte count.
fn dfa_size_limit(&self) -> Result<Option<usize>> { fn dfa_size_limit(&self) -> anyhow::Result<Option<usize>> {
let r = self.parse_human_readable_size("dfa-size-limit")?; let r = self.parse_human_readable_size("dfa-size-limit")?;
u64_to_usize("dfa-size-limit", r) u64_to_usize("dfa-size-limit", r)
} }
@ -1072,7 +1074,7 @@ impl ArgMatches {
/// if set to automatic, the Searcher will do BOM sniffing for UTF-16 /// if set to automatic, the Searcher will do BOM sniffing for UTF-16
/// and transcode seamlessly. If disabled, no BOM sniffing nor transcoding /// and transcode seamlessly. If disabled, no BOM sniffing nor transcoding
/// will occur. /// will occur.
fn encoding(&self) -> Result<EncodingMode> { fn encoding(&self) -> anyhow::Result<EncodingMode> {
if self.is_present("no-encoding") { if self.is_present("no-encoding") {
return Ok(EncodingMode::Auto); return Ok(EncodingMode::Auto);
} }
@ -1092,7 +1094,7 @@ impl ArgMatches {
} }
/// Return the file separator to use based on the CLI configuration. /// Return the file separator to use based on the CLI configuration.
fn file_separator(&self) -> Result<Option<Vec<u8>>> { fn file_separator(&self) -> anyhow::Result<Option<Vec<u8>>> {
// File separators are only used for the standard grep-line format. // File separators are only used for the standard grep-line format.
if self.output_kind() != OutputKind::Standard { if self.output_kind() != OutputKind::Standard {
return Ok(None); return Ok(None);
@ -1130,7 +1132,7 @@ impl ArgMatches {
/// for the current system is used if the value is not set. /// for the current system is used if the value is not set.
/// ///
/// If an invalid pattern is provided, then an error is returned. /// If an invalid pattern is provided, then an error is returned.
fn hyperlink_config(&self) -> Result<HyperlinkConfig> { fn hyperlink_config(&self) -> anyhow::Result<HyperlinkConfig> {
let mut env = HyperlinkEnvironment::new(); let mut env = HyperlinkEnvironment::new();
env.host(hostname(self.value_of_os("hostname-bin"))) env.host(hostname(self.value_of_os("hostname-bin")))
.wsl_prefix(wsl_prefix()); .wsl_prefix(wsl_prefix());
@ -1140,8 +1142,7 @@ impl ArgMatches {
Some(format) => match format.parse() { Some(format) => match format.parse() {
Ok(format) => format, Ok(format) => format,
Err(err) => { Err(err) => {
let msg = format!("invalid hyperlink format: {err}"); anyhow::bail!("invalid hyperlink format: {err}");
return Err(msg.into());
} }
}, },
}; };
@ -1204,7 +1205,7 @@ impl ArgMatches {
/// The maximum number of columns allowed on each line. /// The maximum number of columns allowed on each line.
/// ///
/// If `0` is provided, then this returns `None`. /// If `0` is provided, then this returns `None`.
fn max_columns(&self) -> Result<Option<u64>> { fn max_columns(&self) -> anyhow::Result<Option<u64>> {
Ok(self.usize_of_nonzero("max-columns")?.map(|n| n as u64)) Ok(self.usize_of_nonzero("max-columns")?.map(|n| n as u64))
} }
@ -1215,12 +1216,12 @@ impl ArgMatches {
} }
/// The maximum number of matches permitted. /// The maximum number of matches permitted.
fn max_count(&self) -> Result<Option<u64>> { fn max_count(&self) -> anyhow::Result<Option<u64>> {
Ok(self.usize_of("max-count")?.map(|n| n as u64)) Ok(self.usize_of("max-count")?.map(|n| n as u64))
} }
/// Parses the max-filesize argument option into a byte count. /// Parses the max-filesize argument option into a byte count.
fn max_file_size(&self) -> Result<Option<u64>> { fn max_file_size(&self) -> anyhow::Result<Option<u64>> {
self.parse_human_readable_size("max-filesize") self.parse_human_readable_size("max-filesize")
} }
@ -1311,7 +1312,7 @@ impl ArgMatches {
} }
/// Builds the set of glob overrides from the command line flags. /// Builds the set of glob overrides from the command line flags.
fn overrides(&self) -> Result<Override> { fn overrides(&self) -> anyhow::Result<Override> {
let globs = self.values_of_lossy_vec("glob"); let globs = self.values_of_lossy_vec("glob");
let iglobs = self.values_of_lossy_vec("iglob"); let iglobs = self.values_of_lossy_vec("iglob");
if globs.is_empty() && iglobs.is_empty() { if globs.is_empty() && iglobs.is_empty() {
@ -1378,7 +1379,7 @@ impl ArgMatches {
/// ///
/// If the provided path separator is more than a single byte, then an /// If the provided path separator is more than a single byte, then an
/// error is returned. /// error is returned.
fn path_separator(&self) -> Result<Option<u8>> { fn path_separator(&self) -> anyhow::Result<Option<u8>> {
let sep = match self.value_of_os("path-separator") { let sep = match self.value_of_os("path-separator") {
None => return Ok(None), None => return Ok(None),
Some(sep) => cli::unescape_os(&sep), Some(sep) => cli::unescape_os(&sep),
@ -1386,14 +1387,14 @@ impl ArgMatches {
if sep.is_empty() { if sep.is_empty() {
Ok(None) Ok(None)
} else if sep.len() > 1 { } else if sep.len() > 1 {
Err(From::from(format!( anyhow::bail!(
"A path separator must be exactly one byte, but \ "A path separator must be exactly one byte, but \
the given separator is {} bytes: {}\n\ the given separator is {} bytes: {}\n\
In some shells on Windows '/' is automatically \ In some shells on Windows '/' is automatically \
expanded. Use '//' instead.", expanded. Use '//' instead.",
sep.len(), sep.len(),
cli::escape(&sep), cli::escape(&sep),
))) )
} else { } else {
Ok(Some(sep[0])) Ok(Some(sep[0]))
} }
@ -1433,7 +1434,7 @@ impl ArgMatches {
/// This includes reading the -e/--regexp and -f/--file flags. /// This includes reading the -e/--regexp and -f/--file flags.
/// ///
/// If any pattern is invalid UTF-8, then an error is returned. /// If any pattern is invalid UTF-8, then an error is returned.
fn patterns(&self) -> Result<Vec<String>> { fn patterns(&self) -> anyhow::Result<Vec<String>> {
if self.is_present("files") || self.is_present("type-list") { if self.is_present("files") || self.is_present("type-list") {
return Ok(vec![]); return Ok(vec![]);
} }
@ -1485,7 +1486,7 @@ impl ArgMatches {
/// if -F/--fixed-strings is set. /// if -F/--fixed-strings is set.
/// ///
/// If the pattern is not valid UTF-8, then an error is returned. /// If the pattern is not valid UTF-8, then an error is returned.
fn pattern_from_os_str(&self, pat: &OsStr) -> Result<String> { fn pattern_from_os_str(&self, pat: &OsStr) -> anyhow::Result<String> {
let s = cli::pattern_from_os(pat)?; let s = cli::pattern_from_os(pat)?;
Ok(self.pattern_from_str(s)) Ok(self.pattern_from_str(s))
} }
@ -1525,7 +1526,7 @@ impl ArgMatches {
/// Builds the set of globs for filtering files to apply to the --pre /// Builds the set of globs for filtering files to apply to the --pre
/// flag. If no --pre-globs are available, then this always returns an /// flag. If no --pre-globs are available, then this always returns an
/// empty set of globs. /// empty set of globs.
fn preprocessor_globs(&self) -> Result<Override> { fn preprocessor_globs(&self) -> anyhow::Result<Override> {
let globs = self.values_of_lossy_vec("pre-glob"); let globs = self.values_of_lossy_vec("pre-glob");
if globs.is_empty() { if globs.is_empty() {
return Ok(Override::empty()); return Ok(Override::empty());
@ -1538,7 +1539,7 @@ impl ArgMatches {
} }
/// Parse the regex-size-limit argument option into a byte count. /// Parse the regex-size-limit argument option into a byte count.
fn regex_size_limit(&self) -> Result<Option<usize>> { fn regex_size_limit(&self) -> anyhow::Result<Option<usize>> {
let r = self.parse_human_readable_size("regex-size-limit")?; let r = self.parse_human_readable_size("regex-size-limit")?;
u64_to_usize("regex-size-limit", r) u64_to_usize("regex-size-limit", r)
} }
@ -1549,7 +1550,7 @@ impl ArgMatches {
} }
/// Returns the sorting criteria based on command line parameters. /// Returns the sorting criteria based on command line parameters.
fn sort_by(&self) -> Result<SortBy> { fn sort_by(&self) -> anyhow::Result<SortBy> {
// For backcompat, continue supporting deprecated --sort-files flag. // For backcompat, continue supporting deprecated --sort-files flag.
if self.is_present("sort-files") { if self.is_present("sort-files") {
return Ok(SortBy::asc(SortByKind::Path)); return Ok(SortBy::asc(SortByKind::Path));
@ -1596,7 +1597,7 @@ impl ArgMatches {
} }
/// Return the number of threads that should be used for parallelism. /// Return the number of threads that should be used for parallelism.
fn threads(&self) -> Result<usize> { fn threads(&self) -> anyhow::Result<usize> {
if self.sort_by()?.kind != SortByKind::None { if self.sort_by()?.kind != SortByKind::None {
return Ok(1); return Ok(1);
} }
@ -1607,7 +1608,7 @@ impl ArgMatches {
} }
/// Builds a file type matcher from the command line flags. /// Builds a file type matcher from the command line flags.
fn types(&self) -> Result<Types> { fn types(&self) -> anyhow::Result<Types> {
let mut builder = TypesBuilder::new(); let mut builder = TypesBuilder::new();
builder.add_defaults(); builder.add_defaults();
for ty in self.values_of_lossy_vec("type-clear") { for ty in self.values_of_lossy_vec("type-clear") {
@ -1667,7 +1668,7 @@ impl ArgMatches {
/// ///
/// If the number is zero, then it is considered absent and `None` is /// If the number is zero, then it is considered absent and `None` is
/// returned. /// returned.
fn usize_of_nonzero(&self, name: &str) -> Result<Option<usize>> { fn usize_of_nonzero(&self, name: &str) -> anyhow::Result<Option<usize>> {
let n = match self.usize_of(name)? { let n = match self.usize_of(name)? {
None => return Ok(None), None => return Ok(None),
Some(n) => n, Some(n) => n,
@ -1677,7 +1678,7 @@ impl ArgMatches {
/// Safely reads an arg value with the given name, and if it's present, /// Safely reads an arg value with the given name, and if it's present,
/// tries to parse it as a usize value. /// tries to parse it as a usize value.
fn usize_of(&self, name: &str) -> Result<Option<usize>> { fn usize_of(&self, name: &str) -> anyhow::Result<Option<usize>> {
match self.value_of_lossy(name) { match self.value_of_lossy(name) {
None => Ok(None), None => Ok(None),
Some(v) => v.parse().map(Some).map_err(From::from), Some(v) => v.parse().map(Some).map_err(From::from),
@ -1691,7 +1692,7 @@ impl ArgMatches {
fn parse_human_readable_size( fn parse_human_readable_size(
&self, &self,
arg_name: &str, arg_name: &str,
) -> Result<Option<u64>> { ) -> anyhow::Result<Option<u64>> {
let size = match self.value_of_lossy(arg_name) { let size = match self.value_of_lossy(arg_name) {
None => return Ok(None), None => return Ok(None),
Some(size) => size, Some(size) => size,
@ -1770,11 +1771,10 @@ and look-around.",
fn suggest_multiline(msg: String) -> String { fn suggest_multiline(msg: String) -> String {
if msg.contains("the literal") && msg.contains("not allowed") { if msg.contains("the literal") && msg.contains("not allowed") {
format!( format!(
"{} "{msg}
Consider enabling multiline mode with the --multiline flag (or -U for short). Consider enabling multiline mode with the --multiline flag (or -U for short).
When multiline mode is enabled, new line characters can be matched.", When multiline mode is enabled, new line characters can be matched.",
msg
) )
} else { } else {
msg msg
@ -1783,18 +1783,16 @@ When multiline mode is enabled, new line characters can be matched.",
/// Convert the result of parsing a human readable file size to a `usize`, /// Convert the result of parsing a human readable file size to a `usize`,
/// failing if the type does not fit. /// failing if the type does not fit.
fn u64_to_usize(arg_name: &str, value: Option<u64>) -> Result<Option<usize>> { fn u64_to_usize(
arg_name: &str,
value: Option<u64>,
) -> anyhow::Result<Option<usize>> {
use std::usize; use std::usize;
let value = match value { let Some(value) = value else { return Ok(None) };
None => return Ok(None), usize::try_from(value)
Some(value) => value, .map_err(|_| anyhow::anyhow!("number too large for {arg_name}"))
}; .map(Some)
if value <= usize::MAX as u64 {
Ok(Some(value as usize))
} else {
Err(From::from(format!("number too large for {}", arg_name)))
}
} }
/// Sorts by an optional parameter. /// Sorts by an optional parameter.
@ -1818,7 +1816,7 @@ fn sort_by_option<T: Ord>(
/// corresponds to a `--help` or `--version` request. In which case, the /// corresponds to a `--help` or `--version` request. In which case, the
/// corresponding output is printed and the current process is exited /// corresponding output is printed and the current process is exited
/// successfully. /// successfully.
fn clap_matches<I, T>(args: I) -> Result<clap::ArgMatches<'static>> fn clap_matches<I, T>(args: I) -> anyhow::Result<clap::ArgMatches<'static>>
where where
I: IntoIterator<Item = T>, I: IntoIterator<Item = T>,
T: Into<OsString> + Clone, T: Into<OsString> + Clone,
@ -1845,7 +1843,7 @@ where
/// a directory that no longer exists. We attempt some fallback mechanisms, /// a directory that no longer exists. We attempt some fallback mechanisms,
/// such as querying the PWD environment variable, but otherwise return an /// such as querying the PWD environment variable, but otherwise return an
/// error. /// error.
fn current_dir() -> Result<PathBuf> { fn current_dir() -> anyhow::Result<PathBuf> {
let err = match env::current_dir() { let err = match env::current_dir() {
Err(err) => err, Err(err) => err,
Ok(cwd) => return Ok(cwd), Ok(cwd) => return Ok(cwd),
@ -1855,12 +1853,10 @@ fn current_dir() -> Result<PathBuf> {
return Ok(PathBuf::from(cwd)); return Ok(PathBuf::from(cwd));
} }
} }
Err(format!( anyhow::bail!(
"failed to get current working directory: {} \ "failed to get current working directory: {err} \
--- did your CWD get deleted?", --- did your CWD get deleted?",
err,
) )
.into())
} }
/// Retrieves the hostname that ripgrep should use wherever a hostname is /// Retrieves the hostname that ripgrep should use wherever a hostname is

View File

@ -53,11 +53,11 @@ pub fn args() -> Vec<OsString> {
/// for each line in addition to successfully parsed arguments. /// for each line in addition to successfully parsed arguments.
fn parse<P: AsRef<Path>>( fn parse<P: AsRef<Path>>(
path: P, path: P,
) -> crate::Result<(Vec<OsString>, Vec<Box<dyn std::error::Error>>)> { ) -> anyhow::Result<(Vec<OsString>, Vec<anyhow::Error>)> {
let path = path.as_ref(); let path = path.as_ref();
match std::fs::File::open(&path) { match std::fs::File::open(&path) {
Ok(file) => parse_reader(file), Ok(file) => parse_reader(file),
Err(err) => Err(From::from(format!("{}: {}", path.display(), err))), Err(err) => anyhow::bail!("{}: {}", path.display(), err),
} }
} }
@ -74,7 +74,7 @@ fn parse<P: AsRef<Path>>(
/// in addition to successfully parsed arguments. /// in addition to successfully parsed arguments.
fn parse_reader<R: std::io::Read>( fn parse_reader<R: std::io::Read>(
rdr: R, rdr: R,
) -> crate::Result<(Vec<OsString>, Vec<Box<dyn std::error::Error>>)> { ) -> anyhow::Result<(Vec<OsString>, Vec<anyhow::Error>)> {
let mut bufrdr = std::io::BufReader::new(rdr); let mut bufrdr = std::io::BufReader::new(rdr);
let (mut args, mut errs) = (vec![], vec![]); let (mut args, mut errs) = (vec![], vec![]);
let mut line_number = 0; let mut line_number = 0;
@ -90,7 +90,7 @@ fn parse_reader<R: std::io::Read>(
args.push(osstr.to_os_string()); args.push(osstr.to_os_string());
} }
Err(err) => { Err(err) => {
errs.push(format!("{}: {}", line_number, err).into()); errs.push(anyhow::anyhow!("{line_number}: {err}"));
} }
} }
Ok(true) Ok(true)

View File

@ -40,16 +40,14 @@ mod subject;
#[global_allocator] #[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
fn main() { fn main() {
if let Err(err) = Args::parse().and_then(try_main) { if let Err(err) = Args::parse().and_then(try_main) {
eprintln_locked!("{}", err); eprintln_locked!("{:#}", err);
std::process::exit(2); std::process::exit(2);
} }
} }
fn try_main(args: Args) -> Result<()> { fn try_main(args: Args) -> anyhow::Result<()> {
use args::Command::*; use args::Command::*;
let matched = match args.command() { let matched = match args.command() {
@ -73,7 +71,7 @@ fn try_main(args: Args) -> Result<()> {
/// The top-level entry point for single-threaded search. This recursively /// The top-level entry point for single-threaded search. This recursively
/// steps through the file list (current directory by default) and searches /// steps through the file list (current directory by default) and searches
/// each file sequentially. /// each file sequentially.
fn search(args: &Args) -> Result<bool> { fn search(args: &Args) -> anyhow::Result<bool> {
/// The meat of the routine is here. This lets us call the same iteration /// The meat of the routine is here. This lets us call the same iteration
/// code over each file regardless of whether we stream over the files /// code over each file regardless of whether we stream over the files
/// as they're produced by the underlying directory traversal or whether /// as they're produced by the underlying directory traversal or whether
@ -82,7 +80,7 @@ fn search(args: &Args) -> Result<bool> {
args: &Args, args: &Args,
subjects: impl Iterator<Item = Subject>, subjects: impl Iterator<Item = Subject>,
started_at: std::time::Instant, started_at: std::time::Instant,
) -> Result<bool> { ) -> anyhow::Result<bool> {
let quit_after_match = args.quit_after_match()?; let quit_after_match = args.quit_after_match()?;
let mut stats = args.stats()?; let mut stats = args.stats()?;
let mut searcher = args.search_worker(args.stdout())?; let mut searcher = args.search_worker(args.stdout())?;
@ -138,9 +136,8 @@ fn search(args: &Args) -> Result<bool> {
/// ///
/// Requesting a sorted output from ripgrep (such as with `--sort path`) will /// Requesting a sorted output from ripgrep (such as with `--sort path`) will
/// automatically disable parallelism and hence sorting is not handled here. /// automatically disable parallelism and hence sorting is not handled here.
fn search_parallel(args: &Args) -> Result<bool> { fn search_parallel(args: &Args) -> anyhow::Result<bool> {
use std::sync::atomic::AtomicBool; use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
use std::sync::atomic::Ordering::SeqCst;
let quit_after_match = args.quit_after_match()?; let quit_after_match = args.quit_after_match()?;
let started_at = Instant::now(); let started_at = Instant::now();
@ -227,7 +224,7 @@ fn eprint_nothing_searched() {
/// The top-level entry point for listing files without searching them. This /// The top-level entry point for listing files without searching them. This
/// recursively steps through the file list (current directory by default) and /// recursively steps through the file list (current directory by default) and
/// prints each path sequentially using a single thread. /// prints each path sequentially using a single thread.
fn files(args: &Args) -> Result<bool> { fn files(args: &Args) -> anyhow::Result<bool> {
/// The meat of the routine is here. This lets us call the same iteration /// The meat of the routine is here. This lets us call the same iteration
/// code over each file regardless of whether we stream over the files /// code over each file regardless of whether we stream over the files
/// as they're produced by the underlying directory traversal or whether /// as they're produced by the underlying directory traversal or whether
@ -235,7 +232,7 @@ fn files(args: &Args) -> Result<bool> {
fn iter( fn iter(
args: &Args, args: &Args,
subjects: impl Iterator<Item = Subject>, subjects: impl Iterator<Item = Subject>,
) -> Result<bool> { ) -> anyhow::Result<bool> {
let quit_after_match = args.quit_after_match()?; let quit_after_match = args.quit_after_match()?;
let mut matched = false; let mut matched = false;
let mut path_printer = args.path_printer(args.stdout())?; let mut path_printer = args.path_printer(args.stdout())?;
@ -276,7 +273,7 @@ fn files(args: &Args) -> Result<bool> {
/// ///
/// Requesting a sorted output from ripgrep (such as with `--sort path`) will /// Requesting a sorted output from ripgrep (such as with `--sort path`) will
/// automatically disable parallelism and hence sorting is not handled here. /// automatically disable parallelism and hence sorting is not handled here.
fn files_parallel(args: &Args) -> Result<bool> { fn files_parallel(args: &Args) -> anyhow::Result<bool> {
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst; use std::sync::atomic::Ordering::SeqCst;
use std::sync::mpsc; use std::sync::mpsc;
@ -328,7 +325,7 @@ fn files_parallel(args: &Args) -> Result<bool> {
} }
/// The top-level entry point for --type-list. /// The top-level entry point for --type-list.
fn types(args: &Args) -> Result<bool> { fn types(args: &Args) -> anyhow::Result<bool> {
let mut count = 0; let mut count = 0;
let mut stdout = args.stdout(); let mut stdout = args.stdout();
for def in args.type_defs()? { for def in args.type_defs()? {
@ -350,9 +347,9 @@ fn types(args: &Args) -> Result<bool> {
} }
/// The top-level entry point for --pcre2-version. /// The top-level entry point for --pcre2-version.
fn pcre2_version(args: &Args) -> Result<bool> { fn pcre2_version(args: &Args) -> anyhow::Result<bool> {
#[cfg(feature = "pcre2")] #[cfg(feature = "pcre2")]
fn imp(args: &Args) -> Result<bool> { fn imp(args: &Args) -> anyhow::Result<bool> {
use grep::pcre2; use grep::pcre2;
let mut stdout = args.stdout(); let mut stdout = args.stdout();
@ -367,7 +364,7 @@ fn pcre2_version(args: &Args) -> Result<bool> {
} }
#[cfg(not(feature = "pcre2"))] #[cfg(not(feature = "pcre2"))]
fn imp(args: &Args) -> Result<bool> { fn imp(args: &Args) -> anyhow::Result<bool> {
let mut stdout = args.stdout(); let mut stdout = args.stdout();
writeln!(stdout, "PCRE2 is not available in this build of ripgrep.")?; writeln!(stdout, "PCRE2 is not available in this build of ripgrep.")?;
Ok(false) Ok(false)

View File

@ -119,7 +119,7 @@ impl SearchWorkerBuilder {
pub fn preprocessor( pub fn preprocessor(
&mut self, &mut self,
cmd: Option<PathBuf>, cmd: Option<PathBuf>,
) -> crate::Result<&mut SearchWorkerBuilder> { ) -> anyhow::Result<&mut SearchWorkerBuilder> {
if let Some(ref prog) = cmd { if let Some(ref prog) = cmd {
let bin = cli::resolve_binary(prog)?; let bin = cli::resolve_binary(prog)?;
self.config.preprocessor = Some(bin); self.config.preprocessor = Some(bin);