Improve general error message formatting

This commit is contained in:
Filip Tibell 2023-03-08 12:56:08 +01:00
parent 8e168d3a9d
commit 99ca1fc230
No known key found for this signature in database
7 changed files with 97 additions and 19 deletions

View file

@ -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

View file

@ -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,

View file

@ -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<ExitCode> {
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
}
}
}

39
packages/lib/src/error.rs Normal file
View file

@ -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 {}

View file

@ -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<String>,
@ -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<str>,
script_contents: impl AsRef<[u8]>,
) -> Result<ExitCode, LuneError> {
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<str>,
script_contents: impl AsRef<[u8]>,
emit_prettified_errors: bool,
) -> Result<ExitCode, LuaError> {
// 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() {

View file

@ -40,14 +40,14 @@ fn can_be_plain_lua_table_key(s: &LuaString) -> bool {
pub fn format_label<S: AsRef<str>>(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()
)
}

View file

@ -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)
}
)* }