cli: Use anyhow and thiserror for errors

This patch updates the CLI to use anyhow and thiserror for error
management.  The main feature that our custom `Error` type was providing
was a _list_ of messages, which would allow us to annotate "lower-level"
errors with more contextual information.  This is exactly what's
provided by anyhow's `Context` trait.

(This is setup work for a future PR that will pull the `config` and
`loader` modules out into separate crates; by using `anyhow` we wouldn't
have to deal with a circular dependency between with the new crates.)
This commit is contained in:
Douglas Creager 2021-06-09 12:32:22 -04:00
parent 9d77561c43
commit d2d01e77e3
33 changed files with 237 additions and 419 deletions

View file

@ -1,4 +1,4 @@
use super::error::{Error, Result};
use anyhow::{anyhow, Context, Error, Result};
use libloading::{Library, Symbol};
use once_cell::unsync::OnceCell;
use regex::{Regex, RegexBuilder};
@ -162,7 +162,7 @@ impl Loader {
// one to use by applying the configurations' content regexes.
else {
let file_contents = fs::read(path)
.map_err(Error::wrap(|| format!("Failed to read path {:?}", path)))?;
.with_context(|| format!("Failed to read path {:?}", path))?;
let file_contents = String::from_utf8_lossy(&file_contents);
let mut best_score = -2isize;
let mut best_configuration_id = None;
@ -250,9 +250,9 @@ impl Loader {
name: String,
}
let mut grammar_file =
fs::File::open(grammar_path).map_err(Error::wrap(|| "Failed to read grammar.json"))?;
fs::File::open(grammar_path).with_context(|| "Failed to read grammar.json")?;
let grammar_json: GrammarJSON = serde_json::from_reader(BufReader::new(&mut grammar_file))
.map_err(Error::wrap(|| "Failed to parse grammar.json"))?;
.with_context(|| "Failed to parse grammar.json")?;
let scanner_path = if scanner_path.exists() {
Some(scanner_path)
@ -283,9 +283,8 @@ impl Loader {
let mut library_path = self.parser_lib_path.join(name);
library_path.set_extension(DYLIB_EXTENSION);
let recompile = needs_recompile(&library_path, &parser_path, &scanner_path).map_err(
Error::wrap(|| "Failed to compare source and binary timestamps"),
)?;
let recompile = needs_recompile(&library_path, &parser_path, &scanner_path)
.with_context(|| "Failed to compare source and binary timestamps")?;
if recompile {
let mut config = cc::Build::new();
@ -336,26 +335,23 @@ impl Loader {
let output = command
.output()
.map_err(Error::wrap(|| "Failed to execute C compiler"))?;
.with_context(|| "Failed to execute C compiler")?;
if !output.status.success() {
return Err(Error::new(format!(
return Err(anyhow!(
"Parser compilation failed.\nStdout: {}\nStderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)));
));
}
}
let library = unsafe { Library::new(&library_path) }.map_err(Error::wrap(|| {
format!("Error opening dynamic library {:?}", &library_path)
}))?;
let library = unsafe { Library::new(&library_path) }
.with_context(|| format!("Error opening dynamic library {:?}", &library_path))?;
let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(name));
let language = unsafe {
let language_fn: Symbol<unsafe extern "C" fn() -> Language> = library
.get(language_fn_name.as_bytes())
.map_err(Error::wrap(|| {
format!("Failed to load symbol {}", language_fn_name)
}))?;
.with_context(|| format!("Failed to load symbol {}", language_fn_name))?;
language_fn()
};
mem::forget(library);
@ -370,8 +366,7 @@ impl Loader {
Err(e) => {
eprintln!(
"Failed to load language for injection string '{}': {}",
string,
e.message()
string, e
);
None
}
@ -380,8 +375,7 @@ impl Loader {
Err(e) => {
eprintln!(
"Failed to load property sheet for injection string '{}': {}",
string,
e.message()
string, e
);
None
}
@ -646,7 +640,7 @@ impl<'a> LanguageConfiguration<'a> {
ranges: &'b Vec<(String, Range<usize>)>,
source: &str,
start_offset: usize,
) -> (&'b str, QueryError) {
) -> Error {
let offset_within_section = error.offset - start_offset;
let (path, range) = ranges
.iter()
@ -657,7 +651,7 @@ impl<'a> LanguageConfiguration<'a> {
.chars()
.filter(|c| *c == '\n')
.count();
(path.as_ref(), error)
Error::from(error).context(format!("Error in query file {:?}", path))
}
fn read_queries(
@ -671,18 +665,16 @@ impl<'a> LanguageConfiguration<'a> {
for path in paths {
let abs_path = self.root_path.join(path);
let prev_query_len = query.len();
query += &fs::read_to_string(&abs_path).map_err(Error::wrap(|| {
format!("Failed to read query file {:?}", path)
}))?;
query += &fs::read_to_string(&abs_path)
.with_context(|| format!("Failed to read query file {:?}", path))?;
path_ranges.push((path.clone(), prev_query_len..query.len()));
}
} else {
let queries_path = self.root_path.join("queries");
let path = queries_path.join(default_path);
if path.exists() {
query = fs::read_to_string(&path).map_err(Error::wrap(|| {
format!("Failed to read query file {:?}", path)
}))?;
query = fs::read_to_string(&path)
.with_context(|| format!("Failed to read query file {:?}", path))?;
path_ranges.push((default_path.to_string(), 0..query.len()));
}
}