refactor(generate)!: include path when available in IO errors
This commit is contained in:
parent
7eb23d9f3c
commit
61c21aa408
2 changed files with 87 additions and 57 deletions
|
|
@ -80,8 +80,8 @@ pub type GenerateResult<T> = Result<T, GenerateError>;
|
|||
pub enum GenerateError {
|
||||
#[error("Error with specified path -- {0}")]
|
||||
GrammarPath(String),
|
||||
#[error("{0}")]
|
||||
IO(String),
|
||||
#[error(transparent)]
|
||||
IO(IoError),
|
||||
#[cfg(feature = "load")]
|
||||
#[error(transparent)]
|
||||
LoadGrammarFile(#[from] LoadGrammarError),
|
||||
|
|
@ -100,9 +100,28 @@ pub enum GenerateError {
|
|||
SuperTypeCycle(#[from] SuperTypeCycleError),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for GenerateError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::IO(value.to_string())
|
||||
#[derive(Debug, Error, Serialize)]
|
||||
pub struct IoError {
|
||||
pub error: String,
|
||||
pub path: Option<String>,
|
||||
}
|
||||
|
||||
impl IoError {
|
||||
fn new(error: &std::io::Error, path: Option<&Path>) -> Self {
|
||||
Self {
|
||||
error: error.to_string(),
|
||||
path: path.map(|p| p.to_string_lossy().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IoError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.error)?;
|
||||
if let Some(ref path) = self.path {
|
||||
write!(f, " ({path})")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,18 +136,11 @@ pub enum LoadGrammarError {
|
|||
#[error("Failed to load grammar.js -- {0}")]
|
||||
LoadJSGrammarFile(#[from] JSError),
|
||||
#[error("Failed to load grammar.json -- {0}")]
|
||||
IO(String),
|
||||
IO(IoError),
|
||||
#[error("Unknown grammar file extension: {0:?}")]
|
||||
FileExtension(PathBuf),
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
impl From<std::io::Error> for LoadGrammarError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::IO(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
#[derive(Debug, Error, Serialize)]
|
||||
pub enum ParseVersionError {
|
||||
|
|
@ -136,8 +148,8 @@ pub enum ParseVersionError {
|
|||
Version(String),
|
||||
#[error("{0}")]
|
||||
JSON(String),
|
||||
#[error("{0}")]
|
||||
IO(String),
|
||||
#[error(transparent)]
|
||||
IO(IoError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
|
|
@ -152,8 +164,21 @@ pub enum JSError {
|
|||
JSRuntimeUtf8 { runtime: String, error: String },
|
||||
#[error("`{runtime}` process exited with status {code}")]
|
||||
JSRuntimeExit { runtime: String, code: i32 },
|
||||
#[error("{0}")]
|
||||
IO(String),
|
||||
#[error("Failed to open stdin for `{runtime}`")]
|
||||
JSRuntimeStdin { runtime: String },
|
||||
#[error("Failed to write {item} to `{runtime}`'s stdin -- {error}")]
|
||||
JSRuntimeWrite {
|
||||
runtime: String,
|
||||
item: String,
|
||||
error: String,
|
||||
},
|
||||
#[error("Failed to read output from `{runtime}` -- {error}")]
|
||||
JSRuntimeRead { runtime: String, error: String },
|
||||
#[error(transparent)]
|
||||
IO(IoError),
|
||||
#[cfg(feature = "qjs-rt")]
|
||||
#[error("Failed to get relative path")]
|
||||
RelativePath,
|
||||
#[error("Could not parse this package's version as semver -- {0}")]
|
||||
Semver(String),
|
||||
#[error("Failed to serialze grammar JSON -- {0}")]
|
||||
|
|
@ -163,13 +188,6 @@ pub enum JSError {
|
|||
QuickJS(String),
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
impl From<std::io::Error> for JSError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Self::IO(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
impl From<serde_json::Error> for JSError {
|
||||
fn from(value: serde_json::Error) -> Self {
|
||||
|
|
@ -230,7 +248,8 @@ where
|
|||
.try_exists()
|
||||
.map_err(|e| GenerateError::GrammarPath(e.to_string()))?
|
||||
{
|
||||
fs::create_dir_all(&path_buf)?;
|
||||
fs::create_dir_all(&path_buf)
|
||||
.map_err(|e| GenerateError::IO(IoError::new(&e, Some(path_buf.as_path()))))?;
|
||||
repo_path = path_buf;
|
||||
repo_path.join("grammar.js")
|
||||
} else {
|
||||
|
|
@ -247,15 +266,12 @@ where
|
|||
let header_path = src_path.join("tree_sitter");
|
||||
|
||||
// Ensure that the output directory exists
|
||||
fs::create_dir_all(&src_path)?;
|
||||
fs::create_dir_all(&src_path)
|
||||
.map_err(|e| GenerateError::IO(IoError::new(&e, Some(src_path.as_path()))))?;
|
||||
|
||||
if grammar_path.file_name().unwrap() != "grammar.json" {
|
||||
fs::write(src_path.join("grammar.json"), &grammar_json).map_err(|e| {
|
||||
GenerateError::IO(format!(
|
||||
"Failed to write grammar.json to {} -- {e}",
|
||||
src_path.display()
|
||||
))
|
||||
})?;
|
||||
fs::write(src_path.join("grammar.json"), &grammar_json)
|
||||
.map_err(|e| GenerateError::IO(IoError::new(&e, Some(src_path.as_path()))))?;
|
||||
}
|
||||
|
||||
// If our job is only to generate `grammar.json` and not `parser.c`, stop here.
|
||||
|
|
@ -297,7 +313,8 @@ where
|
|||
|
||||
write_file(&src_path.join("parser.c"), c_code)?;
|
||||
write_file(&src_path.join("node-types.json"), node_types_json)?;
|
||||
fs::create_dir_all(&header_path)?;
|
||||
fs::create_dir_all(&header_path)
|
||||
.map_err(|e| GenerateError::IO(IoError::new(&e, Some(header_path.as_path()))))?;
|
||||
write_file(&header_path.join("alloc.h"), ALLOC_HEADER)?;
|
||||
write_file(&header_path.join("array.h"), ARRAY_HEADER)?;
|
||||
write_file(&header_path.join("parser.h"), PARSER_HEADER)?;
|
||||
|
|
@ -413,9 +430,8 @@ fn read_grammar_version(repo_path: &Path) -> Result<Option<Version>, ParseVersio
|
|||
let json = path
|
||||
.exists()
|
||||
.then(|| {
|
||||
let contents = fs::read_to_string(path.as_path()).map_err(|e| {
|
||||
ParseVersionError::IO(format!("Failed to read `{}` -- {e}", path.display()))
|
||||
})?;
|
||||
let contents = fs::read_to_string(path.as_path())
|
||||
.map_err(|e| ParseVersionError::IO(IoError::new(&e, Some(path.as_path()))))?;
|
||||
serde_json::from_str::<TreeSitterJson>(&contents).map_err(|e| {
|
||||
ParseVersionError::JSON(format!("Failed to parse `{}` -- {e}", path.display()))
|
||||
})
|
||||
|
|
@ -449,14 +465,16 @@ pub fn load_grammar_file(
|
|||
}
|
||||
match grammar_path.extension().and_then(|e| e.to_str()) {
|
||||
Some("js") => Ok(load_js_grammar_file(grammar_path, js_runtime)?),
|
||||
Some("json") => Ok(fs::read_to_string(grammar_path)?),
|
||||
Some("json") => Ok(fs::read_to_string(grammar_path)
|
||||
.map_err(|e| LoadGrammarError::IO(IoError::new(&e, Some(grammar_path))))?),
|
||||
_ => Err(LoadGrammarError::FileExtension(grammar_path.to_owned()))?,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "load")]
|
||||
fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResult<String> {
|
||||
let grammar_path = dunce::canonicalize(grammar_path)?;
|
||||
let grammar_path = dunce::canonicalize(grammar_path)
|
||||
.map_err(|e| JSError::IO(IoError::new(&e, Some(grammar_path))))?;
|
||||
|
||||
#[cfg(feature = "qjs-rt")]
|
||||
if js_runtime == Some("native") {
|
||||
|
|
@ -497,7 +515,9 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
|
|||
let mut js_stdin = js_process
|
||||
.stdin
|
||||
.take()
|
||||
.ok_or_else(|| JSError::IO(format!("Failed to open stdin for `{js_runtime}`")))?;
|
||||
.ok_or_else(|| JSError::JSRuntimeStdin {
|
||||
runtime: js_runtime.to_string(),
|
||||
})?;
|
||||
|
||||
let cli_version = Version::parse(env!("CARGO_PKG_VERSION"))?;
|
||||
write!(
|
||||
|
|
@ -507,21 +527,26 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
|
|||
globalThis.TREE_SITTER_CLI_VERSION_PATCH = {};",
|
||||
cli_version.major, cli_version.minor, cli_version.patch,
|
||||
)
|
||||
.map_err(|e| {
|
||||
JSError::IO(format!(
|
||||
"Failed to write tree-sitter version to `{js_runtime}`'s stdin -- {e}"
|
||||
))
|
||||
})?;
|
||||
js_stdin.write(include_bytes!("./dsl.js")).map_err(|e| {
|
||||
JSError::IO(format!(
|
||||
"Failed to write grammar dsl to `{js_runtime}`'s stdin -- {e}"
|
||||
))
|
||||
.map_err(|e| JSError::JSRuntimeWrite {
|
||||
runtime: js_runtime.to_string(),
|
||||
item: "tree-sitter version".to_string(),
|
||||
error: e.to_string(),
|
||||
})?;
|
||||
js_stdin
|
||||
.write(include_bytes!("./dsl.js"))
|
||||
.map_err(|e| JSError::JSRuntimeWrite {
|
||||
runtime: js_runtime.to_string(),
|
||||
item: "grammar dsl".to_string(),
|
||||
error: e.to_string(),
|
||||
})?;
|
||||
drop(js_stdin);
|
||||
|
||||
let output = js_process
|
||||
.wait_with_output()
|
||||
.map_err(|e| JSError::IO(format!("Failed to read output from `{js_runtime}` -- {e}")))?;
|
||||
.map_err(|e| JSError::JSRuntimeRead {
|
||||
runtime: js_runtime.to_string(),
|
||||
error: e.to_string(),
|
||||
})?;
|
||||
match output.status.code() {
|
||||
Some(0) => {
|
||||
let stdout = String::from_utf8(output.stdout).map_err(|e| JSError::JSRuntimeUtf8 {
|
||||
|
|
@ -537,9 +562,15 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
|
|||
grammar_json = &stdout[pos + 1..];
|
||||
|
||||
let mut stdout = std::io::stdout().lock();
|
||||
stdout.write_all(node_output.as_bytes())?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
stdout
|
||||
.write_all(node_output.as_bytes())
|
||||
.map_err(|e| JSError::IO(IoError::new(&e, None)))?;
|
||||
stdout
|
||||
.write_all(b"\n")
|
||||
.map_err(|e| JSError::IO(IoError::new(&e, None)))?;
|
||||
stdout
|
||||
.flush()
|
||||
.map_err(|e| JSError::IO(IoError::new(&e, None)))?;
|
||||
}
|
||||
|
||||
Ok(serde_json::to_string_pretty(&serde_json::from_str::<
|
||||
|
|
@ -559,8 +590,7 @@ fn load_js_grammar_file(grammar_path: &Path, js_runtime: Option<&str>) -> JSResu
|
|||
|
||||
#[cfg(feature = "load")]
|
||||
pub fn write_file(path: &Path, body: impl AsRef<[u8]>) -> GenerateResult<()> {
|
||||
fs::write(path, body)
|
||||
.map_err(|e| GenerateError::IO(format!("Failed to write {:?} -- {e}", path.file_name())))
|
||||
fs::write(path, body).map_err(|e| GenerateError::IO(IoError::new(&e, Some(path))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rquickjs::{
|
|||
Context, Ctx, Function, Module, Object, Runtime, Type, Value,
|
||||
};
|
||||
|
||||
use super::{JSError, JSResult};
|
||||
use super::{IoError, JSError, JSResult};
|
||||
|
||||
const DSL: &[u8] = include_bytes!("dsl.js");
|
||||
|
||||
|
|
@ -266,10 +266,10 @@ pub fn execute_native_runtime(grammar_path: &Path) -> JSResult<String> {
|
|||
let loader = ScriptLoader::default().with_extension("mjs");
|
||||
runtime.set_loader(resolver, loader);
|
||||
|
||||
let cwd = std::env::current_dir()?;
|
||||
let cwd = std::env::current_dir().map_err(|e| JSError::IO(IoError::new(&e, None)))?;
|
||||
let relative_path = pathdiff::diff_paths(grammar_path, &cwd)
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.ok_or_else(|| JSError::IO("Failed to get relative path".to_string()))?;
|
||||
.ok_or(JSError::RelativePath)?;
|
||||
|
||||
context.with(|ctx| -> JSResult<String> {
|
||||
let globals = ctx.globals();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue