From 99ca1fc230bedf04ec24f45ae2c846c4f3bac5db Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Wed, 8 Mar 2023 12:56:08 +0100 Subject: [PATCH] Improve general error message formatting --- CHANGELOG.md | 1 + packages/cli/src/cli.rs | 10 +++--- packages/cli/src/main.rs | 17 ++++++++-- packages/lib/src/error.rs | 39 +++++++++++++++++++++ packages/lib/src/lib.rs | 43 +++++++++++++++++++----- packages/lib/src/lua/stdio/formatting.rs | 4 +-- packages/lib/src/tests.rs | 2 +- 7 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 packages/lib/src/error.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 658561a..7e9ecc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved error messages for passing invalid file names / file paths substantially - they now include helpful formatting to make file names distinct from file extensions, and give suggestions on how to solve the problem +- Improved general formatting of error messages, both in the CLI and for Luau scripts being run ### Fixed diff --git a/packages/cli/src/cli.rs b/packages/cli/src/cli.rs index b8a2892..97fe11b 100644 --- a/packages/cli/src/cli.rs +++ b/packages/cli/src/cli.rs @@ -175,11 +175,13 @@ impl Cli { (file_display_name, file_contents) }; // Create a new lune object with all globals & run the script - let lune = Lune::new().with_args(self.script_args); - let result = lune.run(&script_display_name, &script_contents).await; + let result = Lune::new() + .with_args(self.script_args) + .try_run(&script_display_name, &script_contents) + .await; Ok(match result { - Err(e) => { - eprintln!("{e}"); + Err(err) => { + eprintln!("{err}"); ExitCode::FAILURE } Ok(code) => code, diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 9b57b5e..842bc4d 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -10,7 +10,6 @@ use std::process::ExitCode; -use anyhow::Result; use clap::Parser; pub(crate) mod cli; @@ -21,8 +20,20 @@ pub(crate) mod utils; mod tests; use cli::Cli; +use console::style; #[tokio::main(flavor = "multi_thread")] -async fn main() -> Result { - Cli::parse().run().await +async fn main() -> ExitCode { + match Cli::parse().run().await { + Ok(code) => code, + Err(err) => { + eprintln!( + "{}{}{}\n{err:?}", + style("[").dim(), + style("ERROR").red(), + style("]").dim(), + ); + ExitCode::FAILURE + } + } } diff --git a/packages/lib/src/error.rs b/packages/lib/src/error.rs new file mode 100644 index 0000000..068738d --- /dev/null +++ b/packages/lib/src/error.rs @@ -0,0 +1,39 @@ +use std::{ + error::Error, + fmt::{Debug, Display, Formatter, Result as FmtResult}, +}; + +use mlua::prelude::*; + +use crate::lua::stdio::formatting::pretty_format_luau_error; + +/** + An opaque error type for formatted lua errors. +*/ +#[derive(Debug, Clone)] +pub struct LuneError { + message: String, +} + +#[allow(dead_code)] +impl LuneError { + pub(crate) fn new(message: String) -> Self { + Self { message } + } + + pub(crate) fn from_lua_error(error: LuaError) -> Self { + Self::new(pretty_format_luau_error(&error, true)) + } + + pub(crate) fn from_lua_error_plain(error: LuaError) -> Self { + Self::new(pretty_format_luau_error(&error, false)) + } +} + +impl Display for LuneError { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{}", self.message) + } +} + +impl Error for LuneError {} diff --git a/packages/lib/src/lib.rs b/packages/lib/src/lib.rs index 0563147..b38f9da 100644 --- a/packages/lib/src/lib.rs +++ b/packages/lib/src/lib.rs @@ -7,14 +7,14 @@ use tokio::task::LocalSet; pub(crate) mod globals; pub(crate) mod lua; +mod error; #[cfg(test)] mod tests; +pub use error::LuneError; pub use globals::LuneGlobal; pub use lua::create_lune_lua; -use lua::stdio::formatting::pretty_format_luau_error; - #[derive(Clone, Debug, Default)] pub struct Lune { args: Vec, @@ -52,24 +52,45 @@ impl Lune { both live for the remainer of the program, and that this leaks memory using [`Box::leak`] that will then get deallocated when the program exits. */ + pub async fn try_run( + &self, + script_name: impl AsRef, + script_contents: impl AsRef<[u8]>, + ) -> Result { + self.run(script_name, script_contents, true) + .await + .map_err(LuneError::from_lua_error) + } + + /** + Tries to run a Lune script directly, returning the underlying + result type if loading the script raises a lua error. + + Passing `false` as the third argument `emit_prettified_errors` will + bypass any additional error formatting automatically done by Lune + for errors that are emitted while the script is running. + + Behavior is otherwise exactly the same as `try_run` + and `try_run` should be preferred in all other cases. + */ + #[doc(hidden)] pub async fn run( &self, script_name: impl AsRef, script_contents: impl AsRef<[u8]>, + emit_prettified_errors: bool, ) -> Result { // Create our special lune-flavored Lua object with extra registry values - let lua = create_lune_lua().expect("Failed to create Lua object"); + let lua = create_lune_lua()?; // Create our task scheduler let sched = TaskScheduler::new(lua)?.into_static(); lua.set_app_data(sched); // Create the main thread and schedule it let main_chunk = lua .load(script_contents.as_ref()) - .set_name(script_name.as_ref()) - .unwrap() - .into_function() - .unwrap(); - let main_thread = lua.create_thread(main_chunk).unwrap(); + .set_name(script_name.as_ref())? + .into_function()?; + let main_thread = lua.create_thread(main_chunk)?; let main_thread_args = LuaValue::Nil.to_lua_multi(lua)?; sched.schedule_blocking(main_thread, main_thread_args)?; // Create our wanted lune globals, some of these need @@ -85,7 +106,11 @@ impl Lune { loop { let result = sched.resume_queue().await; if let Some(err) = result.get_lua_error() { - eprintln!("{}", pretty_format_luau_error(&err, true)); + if emit_prettified_errors { + eprintln!("{}", LuneError::from_lua_error(err)); + } else { + eprintln!("{err}"); + } got_error = true; } if result.is_done() { diff --git a/packages/lib/src/lua/stdio/formatting.rs b/packages/lib/src/lua/stdio/formatting.rs index 042fed2..e91e076 100644 --- a/packages/lib/src/lua/stdio/formatting.rs +++ b/packages/lib/src/lua/stdio/formatting.rs @@ -40,14 +40,14 @@ fn can_be_plain_lua_table_key(s: &LuaString) -> bool { pub fn format_label>(s: S) -> String { format!( "{}{}{} ", - style("[").bold(), + style("[").dim(), match s.as_ref().to_ascii_lowercase().as_str() { "info" => style("INFO").blue(), "warn" => style("WARN").yellow(), "error" => style("ERROR").red(), _ => style(""), }, - style("]").bold() + style("]").dim() ) } diff --git a/packages/lib/src/tests.rs b/packages/lib/src/tests.rs index fb2dc63..c30117d 100644 --- a/packages/lib/src/tests.rs +++ b/packages/lib/src/tests.rs @@ -37,7 +37,7 @@ macro_rules! create_tests { .trim_end_matches(".luau") .trim_end_matches(".lua") .to_string(); - let exit_code = lune.run(&script_name, &script).await?; + let exit_code = lune.try_run(&script_name, &script).await?; Ok(exit_code) } )* }