From a2f834d84684026cd1218a6901606682d1c5f3a1 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Sun, 30 Jul 2023 20:43:52 +0300 Subject: [PATCH] More error contexts + conv panics to errors with context --- cli/src/generate/mod.rs | 53 ++++++++++++++++++++++++----------------- cli/src/main.rs | 26 ++++++++++---------- cli/src/playground.rs | 47 ++++++++++++++++++++---------------- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/cli/src/generate/mod.rs b/cli/src/generate/mod.rs index 206b74f9..4e1cac63 100644 --- a/cli/src/generate/mod.rs +++ b/cli/src/generate/mod.rs @@ -48,22 +48,22 @@ pub fn generate_parser_in_directory( let src_path = repo_path.join("src"); let header_path = src_path.join("tree_sitter"); + // Read the grammar.json. + let grammar_json = match grammar_path { + Some(path) => load_grammar_file(path.as_ref())?, + None => { + let grammar_js_path = grammar_path.map_or(repo_path.join("grammar.js"), |s| s.into()); + load_grammar_file(&grammar_js_path)? + } + }; + // Ensure that the output directories exist. fs::create_dir_all(&src_path)?; fs::create_dir_all(&header_path)?; - // Read the grammar.json. - let grammar_json; - match grammar_path { - Some(path) => { - grammar_json = load_grammar_file(path.as_ref())?; - } - None => { - let grammar_js_path = grammar_path.map_or(repo_path.join("grammar.js"), |s| s.into()); - grammar_json = load_grammar_file(&grammar_js_path)?; - fs::write(&src_path.join("grammar.json"), &grammar_json) - .with_context(|| format!("Failed to write grammar.json to {:?}", src_path))?; - } + if grammar_path.is_none() { + fs::write(&src_path.join("grammar.json"), &grammar_json) + .with_context(|| format!("Failed to write grammar.json to {:?}", src_path))?; } // Parse and preprocess the grammar. @@ -157,9 +157,18 @@ fn generate_parser_for_grammar_with_opts( } pub fn load_grammar_file(grammar_path: &Path) -> Result { + if grammar_path.is_dir() { + return Err(anyhow!( + "Path to a grammar file with `.js` or `.json` extension is required" + )); + } match grammar_path.extension().and_then(|e| e.to_str()) { - Some("js") => Ok(load_js_grammar_file(grammar_path)?), - Some("json") => Ok(fs::read_to_string(grammar_path)?), + Some("js") => { + Ok(load_js_grammar_file(grammar_path).with_context(|| "Failed to load grammar.js")?) + } + Some("json") => { + Ok(fs::read_to_string(grammar_path).with_context(|| "Failed to load grammar.json")?) + } _ => Err(anyhow!( "Unknown grammar file extension: {:?}", grammar_path @@ -174,14 +183,14 @@ fn load_js_grammar_file(grammar_path: &Path) -> Result { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() - .expect("Failed to run `node`"); + .with_context(|| "Failed to run `node`")?; let mut node_stdin = node_process .stdin .take() - .expect("Failed to open stdin for node"); + .with_context(|| "Failed to open stdin for node")?; let cli_version = Version::parse(env!("CARGO_PKG_VERSION")) - .expect("Could not parse this package's version as semver."); + .with_context(|| "Could not parse this package's version as semver.")?; write!( node_stdin, "global.TREE_SITTER_CLI_VERSION_MAJOR = {}; @@ -189,22 +198,22 @@ fn load_js_grammar_file(grammar_path: &Path) -> Result { global.TREE_SITTER_CLI_VERSION_PATCH = {};", cli_version.major, cli_version.minor, cli_version.patch, ) - .expect("Failed to write tree-sitter version to node's stdin"); + .with_context(|| "Failed to write tree-sitter version to node's stdin")?; let javascript_code = include_bytes!("./dsl.js"); node_stdin .write(javascript_code) - .expect("Failed to write grammar dsl to node's stdin"); + .with_context(|| "Failed to write grammar dsl to node's stdin")?; drop(node_stdin); let output = node_process .wait_with_output() - .expect("Failed to read output from node"); + .with_context(|| "Failed to read output from node")?; match output.status.code() { None => panic!("Node process was killed"), Some(0) => {} Some(code) => return Err(anyhow!("Node process exited with status {}", code)), } - - let mut result = String::from_utf8(output.stdout).expect("Got invalid UTF8 from node"); + let mut result = + String::from_utf8(output.stdout).with_context(|| "Got invalid UTF8 from node")?; result.push('\n'); Ok(result) } diff --git a/cli/src/main.rs b/cli/src/main.rs index 7d2f6a17..1912762d 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Context, Error, Result}; use clap::{App, AppSettings, Arg, SubCommand}; use glob::glob; use std::path::{Path, PathBuf}; @@ -317,16 +317,18 @@ fn run() -> Result<()> { if matches.is_present("log") { logger::init(); } - let abi_version = - matches - .value_of("abi-version") - .map_or(DEFAULT_GENERATE_ABI_VERSION, |version| { - if version == "latest" { - tree_sitter::LANGUAGE_VERSION - } else { - version.parse().expect("invalid abi version flag") - } - }); + let abi_version = matches.value_of("abi-version").map_or( + Ok::<_, Error>(DEFAULT_GENERATE_ABI_VERSION), + |version| { + Ok(if version == "latest" { + tree_sitter::LANGUAGE_VERSION + } else { + version + .parse() + .with_context(|| "invalid abi version flag")? + }) + }, + )?; let generate_bindings = !matches.is_present("no-bindings"); generate::generate_parser_in_directory( ¤t_dir, @@ -635,7 +637,7 @@ fn run() -> Result<()> { ("playground", Some(matches)) => { let open_in_browser = !matches.is_present("quiet"); - playground::serve(¤t_dir, open_in_browser); + playground::serve(¤t_dir, open_in_browser)?; } ("dump-languages", Some(_)) => { diff --git a/cli/src/playground.rs b/cli/src/playground.rs index 5bbcb3c3..662eb8aa 100644 --- a/cli/src/playground.rs +++ b/cli/src/playground.rs @@ -1,5 +1,5 @@ use super::wasm; -use anyhow::Context; +use anyhow::{anyhow, Context, Result}; use std::{ borrow::Cow, env, fs, @@ -43,20 +43,17 @@ fn get_main_html(tree_sitter_dir: &Option) -> Cow<'static, [u8]> { } } -pub fn serve(grammar_path: &Path, open_in_browser: bool) { - let server = get_server(); +pub fn serve(grammar_path: &Path, open_in_browser: bool) -> Result<()> { + let server = get_server()?; let grammar_name = wasm::get_grammar_name(&grammar_path.join("src")) - .with_context(|| "Failed to get wasm filename") - .unwrap(); + .with_context(|| "Failed to get wasm filename")?; let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name); - let language_wasm = fs::read(grammar_path.join(&wasm_filename)) - .with_context(|| { - format!( - "Failed to read {}. Run `tree-sitter build-wasm` first.", - wasm_filename - ) - }) - .unwrap(); + let language_wasm = fs::read(grammar_path.join(&wasm_filename)).with_context(|| { + format!( + "Failed to read {}. Run `tree-sitter build-wasm` first.", + wasm_filename + ) + })?; let url = format!("http://{}", server.server_addr()); println!("Started playground on: {}", url); if open_in_browser { @@ -105,8 +102,12 @@ pub fn serve(grammar_path: &Path, open_in_browser: bool) { } _ => response(b"Not found", &html_header).with_status_code(404), }; - request.respond(res).expect("Failed to write HTTP response"); + request + .respond(res) + .with_context(|| "Failed to write HTTP response")?; } + + Ok(()) } fn redirect<'a>(url: &'a str) -> Response<&'a [u8]> { @@ -121,18 +122,24 @@ fn response<'a>(data: &'a [u8], header: &Header) -> Response<&'a [u8]> { .with_header(header.clone()) } -fn get_server() -> Server { +fn get_server() -> Result { let addr = env::var("TREE_SITTER_PLAYGROUND_ADDR").unwrap_or("127.0.0.1".to_owned()); let port = env::var("TREE_SITTER_PLAYGROUND_PORT") - .map(|v| v.parse::().expect("Invalid port specification")) + .map(|v| { + v.parse::() + .with_context(|| "Invalid port specification") + }) .ok(); let listener = match port { - Some(port) => bind_to(&*addr, port).expect("Can't bind to the specified port"), - None => { - get_listener_on_available_port(&*addr).expect("Can't find a free port to bind to it") + Some(port) => { + bind_to(&*addr, port?).with_context(|| "Failed to bind to the specified port")? } + None => get_listener_on_available_port(&*addr) + .with_context(|| "Failed to find a free port to bind to it")?, }; - Server::from_listener(listener, None).expect("Failed to start web server") + let server = + Server::from_listener(listener, None).map_err(|_| anyhow!("Failed to start web server"))?; + Ok(server) } fn get_listener_on_available_port(addr: &str) -> Option {