diff --git a/CHANGELOG.md b/CHANGELOG.md index 8256221..1f757c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a new global `stdio` which replaces `console` and adds a couple new functions: + - `write` writes a string directly to stdout, without any newlines + - `ewrite` writes a string directly to stderr, without any newlines + ### Changed +- Migrated `console.setColor/resetColor` and `console.setStyle/resetStyle` to `stdio.color` and `stdio.style` to allow for more flexibility in custom printing using ANSI color codes. Check the documentation for new usage and behavior. - Migrated the pretty-printing and formatting behavior of `console.log/info/warn/error` to the standard Luau printing functions. ### Removed - Removed printing functions `console.log/info/warn/error` in favor of regular global functions for printing. +### Fixed + +- Fixed scripts hanging indefinitely on error + ## `0.2.2` - February 5th, 2023 ### Added diff --git a/Cargo.lock b/Cargo.lock index e1f54cc..5c04033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,19 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "console" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.42.0", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -136,6 +149,12 @@ dependencies = [ "syn", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -450,6 +469,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.139" @@ -518,7 +543,9 @@ name = "lune" version = "0.2.2" dependencies = [ "anyhow", + "console", "hyper", + "lazy_static", "mlua", "os_str_bytes", "reqwest", @@ -1134,6 +1161,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/README.md b/README.md index 7402b55..8728e81 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,10 @@ More examples of how to write Lune scripts can be found in the [examples](.lune/
🔎 List of APIs -`console` - Logging & formatting
`fs` - Filesystem
`net` - Networking
`process` - Current process & child processes
+`stdio` - Standard input / output & utility functions
`task` - Task scheduler & thread spawning
Documentation for individual members and types can be found using your editor of choice and [Luau LSP](https://github.com/JohnnyMorganz/luau-lsp). diff --git a/lune.yml b/lune.yml index 6e90e21..5db9349 100644 --- a/lune.yml +++ b/lune.yml @@ -1,19 +1,6 @@ # Lune v0.2.2 --- globals: - # Console - console.resetStyle: - args: [] - console.setStyle: - args: - - required: false - type: string - - required: false - type: string - console.format: - must_use: true - args: - - type: "..." # FS (filesystem) fs.readFile: must_use: true @@ -80,6 +67,25 @@ globals: type: table - required: false type: table + # Stdio + stdio.color: + must_use: true + args: + - type: string + stdio.style: + must_use: true + args: + - type: string + console.format: + must_use: true + args: + - type: "..." + stdio.write: + args: + - type: string + stdio.ewrite: + args: + - type: string # Task task.cancel: args: diff --git a/luneDocs.json b/luneDocs.json index d764046..cc177d5 100644 --- a/luneDocs.json +++ b/luneDocs.json @@ -1,61 +1,4 @@ { - "@roblox/global/console": { - "code_sample": "", - "documentation": "Logging & formatting", - "keys": { - "console": "@roblox/global/console.console" - }, - "learn_more_link": "" - }, - "@roblox/global/console.format": { - "code_sample": "", - "documentation": "Formats arguments into a human-readable string with syntax highlighting for tables.", - "learn_more_link": "", - "params": [ - { - "documentation": "@roblox/global/console.format/param/0", - "name": "..." - } - ], - "returns": [ - "@roblox/global/console.format/return/0" - ] - }, - "@roblox/global/console.format/param/0": { - "documentation": "The values to format" - }, - "@roblox/global/console.format/return/0": { - "documentation": "The formatted string" - }, - "@roblox/global/console.resetStyle": { - "code_sample": "", - "documentation": "Resets the current persistent output color and style.", - "learn_more_link": "", - "params": [], - "returns": [] - }, - "@roblox/global/console.setStyle": { - "code_sample": "", - "documentation": "Sets the current persistent output color and / or style.", - "learn_more_link": "", - "params": [ - { - "documentation": "@roblox/global/console.setStyle/param/0", - "name": "color" - }, - { - "documentation": "@roblox/global/console.setStyle/param/1", - "name": "style" - } - ], - "returns": [] - }, - "@roblox/global/console.setStyle/param/0": { - "documentation": "The color to set" - }, - "@roblox/global/console.setStyle/param/1": { - "documentation": "The style to set" - }, "@roblox/global/fs": { "code_sample": "", "documentation": "Filesystem", @@ -386,6 +329,94 @@ "@roblox/global/process.spawn/return/0": { "documentation": "A dictionary representing the result of the child process" }, + "@roblox/global/stdio": { + "code_sample": "", + "documentation": "Standard input / output & utility functions", + "keys": { + "stdio": "@roblox/global/stdio.stdio" + }, + "learn_more_link": "" + }, + "@roblox/global/stdio.color": { + "code_sample": "", + "documentation": "Return an ANSI string that can be used to modify the persistent output color.\n\nPass `\"reset\"` to get a string that can reset the persistent output color.\n\n### Example usage\n\n```lua\nstdio.write(stdio.color(\"red\"))\nprint(\"This text will be red\")\nstdio.write(stdio.color(\"reset\"))\nprint(\"This text will be normal\")\n```", + "learn_more_link": "", + "params": [ + { + "documentation": "@roblox/global/stdio.color/param/0", + "name": "color" + } + ], + "returns": [] + }, + "@roblox/global/stdio.color/param/0": { + "documentation": "The color to use" + }, + "@roblox/global/stdio.ewrite": { + "code_sample": "", + "documentation": "Writes a string directly to stderr, without any newline.", + "learn_more_link": "", + "params": [ + { + "documentation": "@roblox/global/stdio.ewrite/param/0", + "name": "s" + } + ], + "returns": [] + }, + "@roblox/global/stdio.ewrite/param/0": { + "documentation": "The string to write to stderr" + }, + "@roblox/global/stdio.format": { + "code_sample": "", + "documentation": "Formats arguments into a human-readable string with syntax highlighting for tables.", + "learn_more_link": "", + "params": [ + { + "documentation": "@roblox/global/stdio.format/param/0", + "name": "..." + } + ], + "returns": [ + "@roblox/global/stdio.format/return/0" + ] + }, + "@roblox/global/stdio.format/param/0": { + "documentation": "The values to format" + }, + "@roblox/global/stdio.format/return/0": { + "documentation": "The formatted string" + }, + "@roblox/global/stdio.style": { + "code_sample": "", + "documentation": "Return an ANSI string that can be used to modify the persistent output style.\n\nPass `\"reset\"` to get a string that can reset the persistent output style.\n\n### Example usage\n\n```lua\nstdio.write(stdio.style(\"bold\"))\nprint(\"This text will be bold\")\nstdio.write(stdio.style(\"reset\"))\nprint(\"This text will be normal\")\n```", + "learn_more_link": "", + "params": [ + { + "documentation": "@roblox/global/stdio.style/param/0", + "name": "style" + } + ], + "returns": [] + }, + "@roblox/global/stdio.style/param/0": { + "documentation": "The style to use" + }, + "@roblox/global/stdio.write": { + "code_sample": "", + "documentation": "Writes a string directly to stdout, without any newline.", + "learn_more_link": "", + "params": [ + { + "documentation": "@roblox/global/stdio.write/param/0", + "name": "s" + } + ], + "returns": [] + }, + "@roblox/global/stdio.write/param/0": { + "documentation": "The string to write to stdout" + }, "@roblox/global/task": { "code_sample": "", "documentation": "Task scheduler & thread spawning", diff --git a/luneTypes.d.luau b/luneTypes.d.luau index 4caf960..66739e9 100644 --- a/luneTypes.d.luau +++ b/luneTypes.d.luau @@ -1,40 +1,5 @@ -- Lune v0.2.2 -type ConsoleColor = "black" | "red" | "green" | "yellow" | "blue" | "purple" | "cyan" | "white" -type ConsoleStyle = "bold" | "dim" - ---[=[ - @class console - - Logging & formatting -]=] -declare console: { - --[=[ - @within console - - Resets the current persistent output color and style. - ]=] - resetStyle: () -> (), - --[=[ - @within console - - Sets the current persistent output color and / or style. - - @param color The color to set - @param style The style to set - ]=] - setStyle: (color: ConsoleColor?, style: ConsoleStyle?) -> (), - --[=[ - @within console - - Formats arguments into a human-readable string with syntax highlighting for tables. - - @param ... The values to format - @return The formatted string - ]=] - format: (...any) -> (string), -} - --[=[ @class fs @@ -317,6 +282,77 @@ declare process: { }, } +--[=[ + @class stdio + + Standard input / output & utility functions +]=] +declare stdio: { + --[=[ + @within stdio + + Return an ANSI string that can be used to modify the persistent output color. + + Pass `"reset"` to get a string that can reset the persistent output color. + + ### Example usage + + ```lua + stdio.write(stdio.color("red")) + print("This text will be red") + stdio.write(stdio.color("reset")) + print("This text will be normal") + ``` + + @param color The color to use + ]=] + color: (color: "reset" | "black" | "red" | "green" | "yellow" | "blue" | "purple" | "cyan" | "white") -> (), + --[=[ + @within stdio + + Return an ANSI string that can be used to modify the persistent output style. + + Pass `"reset"` to get a string that can reset the persistent output style. + + ### Example usage + + ```lua + stdio.write(stdio.style("bold")) + print("This text will be bold") + stdio.write(stdio.style("reset")) + print("This text will be normal") + ``` + + @param style The style to use + ]=] + style: (style: "reset" | "bold" | "dim") -> (), + --[=[ + @within stdio + + Formats arguments into a human-readable string with syntax highlighting for tables. + + @param ... The values to format + @return The formatted string + ]=] + format: (...any) -> (string), + --[=[ + @within stdio + + Writes a string directly to stdout, without any newline. + + @param s The string to write to stdout + ]=] + write: (s: string) -> (), + --[=[ + @within stdio + + Writes a string directly to stderr, without any newline. + + @param s The string to write to stderr + ]=] + ewrite: (s: string) -> (), +} + --[=[ @class task diff --git a/packages/lib/Cargo.toml b/packages/lib/Cargo.toml index 719cf6b..25e2972 100644 --- a/packages/lib/Cargo.toml +++ b/packages/lib/Cargo.toml @@ -20,6 +20,8 @@ serde.workspace = true tokio.workspace = true reqwest.workspace = true +console = "0.15.5" +lazy_static = "1.4.0" os_str_bytes = "6.4.1" hyper = { version = "0.14.24", features = ["full"] } diff --git a/packages/lib/src/globals/console.rs b/packages/lib/src/globals/console.rs deleted file mode 100644 index a197ddf..0000000 --- a/packages/lib/src/globals/console.rs +++ /dev/null @@ -1,30 +0,0 @@ -use mlua::prelude::*; - -use crate::utils::{ - formatting::{pretty_format_multi_value, print_color, print_reset, print_style}, - table::TableBuilder, -}; - -pub fn create(lua: &Lua) -> LuaResult<()> { - lua.globals().raw_set( - "console", - TableBuilder::new(lua)? - .with_function("resetStyle", |_, _: ()| print_reset())? - .with_function( - "setStyle", - |_, (color, style): (Option, Option)| { - if let Some(color) = color { - print_color(color)?; - } - if let Some(style) = style { - print_style(style)?; - } - Ok(()) - }, - )? - .with_function("format", |_, args: LuaMultiValue| { - pretty_format_multi_value(&args) - })? - .build_readonly()?, - ) -} diff --git a/packages/lib/src/globals/mod.rs b/packages/lib/src/globals/mod.rs index b0a27ed..7040a81 100644 --- a/packages/lib/src/globals/mod.rs +++ b/packages/lib/src/globals/mod.rs @@ -1,24 +1,24 @@ -mod console; mod fs; mod net; mod process; mod require; +mod stdio; mod task; // Global tables -pub use console::create as create_console; pub use fs::create as create_fs; pub use net::create as create_net; pub use process::create as create_process; pub use require::create as create_require; +pub use stdio::create as create_stdio; pub use task::create as create_task; // Individual top-level global values use mlua::prelude::*; -use crate::utils::formatting::pretty_format_multi_value; +use crate::utils::formatting::{format_label, pretty_format_multi_value}; pub fn create_top_level(lua: &Lua) -> LuaResult<()> { let globals = lua.globals(); @@ -42,28 +42,40 @@ pub fn create_top_level(lua: &Lua) -> LuaResult<()> { globals.raw_set( "info", lua.create_function(|lua, args: LuaMultiValue| { - let formatted = pretty_format_multi_value(&args)?; let print: LuaFunction = lua.named_registry_value("print")?; - print.call(formatted)?; + print.call(format!( + "{}\n{}", + format_label("info"), + pretty_format_multi_value(&args)? + ))?; Ok(()) })?, )?; globals.raw_set( "warn", lua.create_function(|lua, args: LuaMultiValue| { - let formatted = pretty_format_multi_value(&args)?; let print: LuaFunction = lua.named_registry_value("print")?; - print.call(formatted)?; + print.call(format!( + "{}\n{}", + format_label("warn"), + pretty_format_multi_value(&args)? + ))?; Ok(()) })?, )?; globals.raw_set( "error", lua.create_function(|lua, (arg, level): (LuaValue, Option)| { - let multi = arg.to_lua_multi(lua)?; - let formatted = pretty_format_multi_value(&multi)?; let error: LuaFunction = lua.named_registry_value("error")?; - error.call((formatted, level))?; + let multi = arg.to_lua_multi(lua)?; + error.call(( + format!( + "{}\n{}", + format_label("error"), + pretty_format_multi_value(&multi)? + ), + level, + ))?; Ok(()) })?, )?; diff --git a/packages/lib/src/globals/stdio.rs b/packages/lib/src/globals/stdio.rs new file mode 100644 index 0000000..e20fa04 --- /dev/null +++ b/packages/lib/src/globals/stdio.rs @@ -0,0 +1,35 @@ +use mlua::prelude::*; + +use crate::utils::{ + formatting::{ + format_style, pretty_format_multi_value, style_from_color_str, style_from_style_str, + }, + table::TableBuilder, +}; + +pub fn create(lua: &Lua) -> LuaResult<()> { + lua.globals().raw_set( + "stdio", + TableBuilder::new(lua)? + .with_function("color", |_, color: String| { + let ansi_string = format_style(style_from_color_str(&color)?); + Ok(ansi_string) + })? + .with_function("style", |_, style: String| { + let ansi_string = format_style(style_from_style_str(&style)?); + Ok(ansi_string) + })? + .with_function("format", |_, args: LuaMultiValue| { + pretty_format_multi_value(&args) + })? + .with_function("write", |_, s: String| { + print!("{s}"); + Ok(()) + })? + .with_function("ewrite", |_, s: String| { + eprint!("{s}"); + Ok(()) + })? + .build_readonly()?, + ) +} diff --git a/packages/lib/src/lib.rs b/packages/lib/src/lib.rs index 5daf569..5c69acd 100644 --- a/packages/lib/src/lib.rs +++ b/packages/lib/src/lib.rs @@ -8,7 +8,7 @@ pub(crate) mod utils; use crate::{ globals::{ - create_console, create_fs, create_net, create_process, create_require, create_task, + create_fs, create_net, create_process, create_require, create_stdio, create_task, create_top_level, }, utils::formatting::pretty_format_luau_error, @@ -16,11 +16,11 @@ use crate::{ #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum LuneGlobal { - Console, Fs, Net, Process, Require, + Stdio, Task, TopLevel, } @@ -28,11 +28,11 @@ pub enum LuneGlobal { impl LuneGlobal { pub fn get_all() -> Vec { vec![ - Self::Console, Self::Fs, Self::Net, Self::Process, Self::Require, + Self::Stdio, Self::Task, Self::TopLevel, ] @@ -85,11 +85,11 @@ impl Lune { // Add in wanted lune globals for global in &self.globals { match &global { - LuneGlobal::Console => create_console(&lua)?, LuneGlobal::Fs => create_fs(&lua)?, LuneGlobal::Net => create_net(&lua)?, LuneGlobal::Process => create_process(&lua, self.args.clone())?, LuneGlobal::Require => create_require(&lua)?, + LuneGlobal::Stdio => create_stdio(&lua)?, LuneGlobal::Task => create_task(&lua)?, LuneGlobal::TopLevel => create_top_level(&lua)?, } @@ -150,7 +150,7 @@ impl Lune { LuneMessage::LuaError(e) => { eprintln!("{}", pretty_format_luau_error(&e)); got_error = true; - task_count += 1; + task_count -= 1; } }; // If there are no tasks left running, it is now @@ -180,6 +180,8 @@ mod tests { use std::{env::set_current_dir, path::PathBuf, process::ExitCode}; use anyhow::Result; + use console::set_colors_enabled; + use console::set_colors_enabled_stderr; use tokio::fs::read_to_string; use crate::Lune; @@ -191,6 +193,10 @@ mod tests { $( #[tokio::test] async fn $name() -> Result { + // Disable styling for stdout and stderr since + // some tests rely on output not being styled + set_colors_enabled(false); + set_colors_enabled_stderr(false); // NOTE: This path is relative to the lib // package, not the cwd or workspace root, // so we need to cd to the repo root first @@ -218,8 +224,6 @@ mod tests { } run_tests! { - console_format: "console/format", - console_set_style: "console/set_style", fs_files: "fs/files", fs_dirs: "fs/dirs", net_request_codes: "net/request/codes", @@ -238,6 +242,11 @@ mod tests { require_nested: "require/tests/nested", require_parents: "require/tests/parents", require_siblings: "require/tests/siblings", + stdio_format: "stdio/format", + stdio_color: "stdio/color", + stdio_style: "stdio/style", + stdio_write: "stdio/write", + stdio_ewrite: "stdio/ewrite", task_cancel: "task/cancel", task_defer: "task/defer", task_delay: "task/delay", diff --git a/packages/lib/src/utils/formatting.rs b/packages/lib/src/utils/formatting.rs index 3d433ba..7f1e089 100644 --- a/packages/lib/src/utils/formatting.rs +++ b/packages/lib/src/utils/formatting.rs @@ -1,32 +1,28 @@ -use std::{ - fmt::Write as _, - io::{self, Write as _}, -}; +use std::fmt::Write; +use console::{style, Style}; +use lazy_static::lazy_static; use mlua::prelude::*; const MAX_FORMAT_DEPTH: usize = 4; const INDENT: &str = " "; -// TODO: Use some crate for this instead +pub const STYLE_RESET_STR: &str = "\x1b[0m"; -pub const COLOR_RESET: &str = if cfg!(test) { "" } else { "\x1B[0m" }; -pub const COLOR_BLACK: &str = if cfg!(test) { "" } else { "\x1B[30m" }; -pub const COLOR_RED: &str = if cfg!(test) { "" } else { "\x1B[31m" }; -pub const COLOR_GREEN: &str = if cfg!(test) { "" } else { "\x1B[32m" }; -pub const COLOR_YELLOW: &str = if cfg!(test) { "" } else { "\x1B[33m" }; -pub const COLOR_BLUE: &str = if cfg!(test) { "" } else { "\x1B[34m" }; -pub const COLOR_PURPLE: &str = if cfg!(test) { "" } else { "\x1B[35m" }; -pub const COLOR_CYAN: &str = if cfg!(test) { "" } else { "\x1B[36m" }; -pub const COLOR_WHITE: &str = if cfg!(test) { "" } else { "\x1B[37m" }; - -pub const STYLE_RESET: &str = if cfg!(test) { "" } else { "\x1B[22m" }; -pub const STYLE_BOLD: &str = if cfg!(test) { "" } else { "\x1B[1m" }; -pub const STYLE_DIM: &str = if cfg!(test) { "" } else { "\x1B[2m" }; - -pub fn flush_stdout() -> LuaResult<()> { - io::stdout().flush().map_err(LuaError::external) +lazy_static! { + // Colors + pub static ref COLOR_BLACK: Style = Style::new().black(); + pub static ref COLOR_RED: Style = Style::new().red(); + pub static ref COLOR_GREEN: Style = Style::new().green(); + pub static ref COLOR_YELLOW: Style = Style::new().yellow(); + pub static ref COLOR_BLUE: Style = Style::new().blue(); + pub static ref COLOR_PURPLE: Style = Style::new().magenta(); + pub static ref COLOR_CYAN: Style = Style::new().cyan(); + pub static ref COLOR_WHITE: Style = Style::new().white(); + // Styles + pub static ref STYLE_BOLD: Style = Style::new().bold(); + pub static ref STYLE_DIM: Style = Style::new().dim(); } fn can_be_plain_lua_table_key(s: &LuaString) -> bool { @@ -41,74 +37,68 @@ fn can_be_plain_lua_table_key(s: &LuaString) -> bool { pub fn format_label>(s: S) -> String { format!( - "{}[{}{}{}{}]{} ", - STYLE_BOLD, + "{}{}{} ", + style("[").bold(), match s.as_ref().to_ascii_lowercase().as_str() { - "info" => COLOR_BLUE, - "warn" => COLOR_YELLOW, - "error" => COLOR_RED, - _ => COLOR_WHITE, + "info" => style("INFO").blue(), + "warn" => style("WARN").yellow(), + "error" => style("ERROR").red(), + _ => style(""), }, - s.as_ref().to_ascii_uppercase(), - COLOR_RESET, - STYLE_BOLD, - STYLE_RESET + style("]").bold() ) } -pub fn print_label>(s: S) -> LuaResult<()> { - print!("{}", format_label(s)); - flush_stdout()?; - Ok(()) +pub fn format_style(style: Option<&'static Style>) -> String { + if cfg!(test) { + "".to_string() + } else if let Some(style) = style { + // HACK: We have no direct way of referencing the ansi color code + // of the style that console::Style provides, and we also know for + // sure that styles always include the reset sequence at the end + style + .apply_to("") + .to_string() + .strip_suffix(STYLE_RESET_STR) + .unwrap() + .to_string() + } else { + STYLE_RESET_STR.to_string() + } } -pub fn print_style>(s: S) -> LuaResult<()> { - print!( - "{}", - match s.as_ref() { - "reset" => STYLE_RESET, - "bold" => STYLE_BOLD, - "dim" => STYLE_DIM, - _ => { - return Err(LuaError::RuntimeError(format!( - "The style '{}' is not a valid style name", - s.as_ref() - ))); - } +pub fn style_from_color_str>(s: S) -> LuaResult> { + Ok(match s.as_ref() { + "reset" => None, + "black" => Some(&COLOR_BLACK), + "red" => Some(&COLOR_RED), + "green" => Some(&COLOR_GREEN), + "yellow" => Some(&COLOR_YELLOW), + "blue" => Some(&COLOR_BLUE), + "purple" => Some(&COLOR_PURPLE), + "cyan" => Some(&COLOR_CYAN), + "white" => Some(&COLOR_WHITE), + _ => { + return Err(LuaError::RuntimeError(format!( + "The color '{}' is not a valid color name", + s.as_ref() + ))); } - ); - flush_stdout()?; - Ok(()) + }) } -pub fn print_reset() -> LuaResult<()> { - print_style("reset")?; - Ok(()) -} - -pub fn print_color>(s: S) -> LuaResult<()> { - print!( - "{}", - match s.as_ref() { - "reset" => COLOR_RESET, - "black" => COLOR_BLACK, - "red" => COLOR_RED, - "green" => COLOR_GREEN, - "yellow" => COLOR_YELLOW, - "blue" => COLOR_BLUE, - "purple" => COLOR_PURPLE, - "cyan" => COLOR_CYAN, - "white" => COLOR_WHITE, - _ => { - return Err(LuaError::RuntimeError(format!( - "The color '{}' is not a valid color name", - s.as_ref() - ))); - } +pub fn style_from_style_str>(s: S) -> LuaResult> { + Ok(match s.as_ref() { + "reset" => None, + "bold" => Some(&STYLE_BOLD), + "dim" => Some(&STYLE_DIM), + _ => { + return Err(LuaError::RuntimeError(format!( + "The style '{}' is not a valid style name", + s.as_ref() + ))); } - ); - flush_stdout()?; - Ok(()) + }) } pub fn pretty_format_value( @@ -119,65 +109,66 @@ pub fn pretty_format_value( // TODO: Handle tables with cyclic references match &value { LuaValue::Nil => write!(buffer, "nil")?, - LuaValue::Boolean(true) => write!(buffer, "{COLOR_YELLOW}true{COLOR_RESET}")?, - LuaValue::Boolean(false) => write!(buffer, "{COLOR_YELLOW}false{COLOR_RESET}")?, - LuaValue::Number(n) => write!(buffer, "{COLOR_CYAN}{n}{COLOR_RESET}")?, - LuaValue::Integer(i) => write!(buffer, "{COLOR_CYAN}{i}{COLOR_RESET}")?, + LuaValue::Boolean(true) => write!(buffer, "{}", COLOR_YELLOW.apply_to("true"))?, + LuaValue::Boolean(false) => write!(buffer, "{}", COLOR_YELLOW.apply_to("false"))?, + LuaValue::Number(n) => write!(buffer, "{}", COLOR_CYAN.apply_to(format!("{n}")))?, + LuaValue::Integer(i) => write!(buffer, "{}", COLOR_CYAN.apply_to(format!("{i}")))?, LuaValue::String(s) => write!( buffer, - "{}\"{}\"{}", - COLOR_GREEN, - s.to_string_lossy() - .replace('"', r#"\""#) - .replace('\r', r#"\r"#) - .replace('\n', r#"\n"#), - COLOR_RESET + "\"{}\"", + COLOR_GREEN.apply_to( + s.to_string_lossy() + .replace('"', r#"\""#) + .replace('\r', r#"\r"#) + .replace('\n', r#"\n"#) + ) )?, LuaValue::Table(ref tab) => { if depth >= MAX_FORMAT_DEPTH { - write!(buffer, "{STYLE_DIM}{{ ... }}{STYLE_RESET}")?; + write!(buffer, "{}", STYLE_DIM.apply_to("{ ... }"))?; } else { let mut is_empty = false; let depth_indent = INDENT.repeat(depth); - write!(buffer, "{STYLE_DIM}{{{STYLE_RESET}")?; + write!(buffer, "{}", STYLE_DIM.apply_to("{"))?; for pair in tab.clone().pairs::() { let (key, value) = pair.unwrap(); match &key { LuaValue::String(s) if can_be_plain_lua_table_key(s) => write!( buffer, - "\n{}{}{} {}={} ", + "\n{}{}{} {} ", depth_indent, INDENT, s.to_string_lossy(), - STYLE_DIM, - STYLE_RESET + STYLE_DIM.apply_to("=") )?, _ => { write!(buffer, "\n{depth_indent}{INDENT}[")?; pretty_format_value(buffer, &key, depth)?; - write!(buffer, "] {STYLE_DIM}={STYLE_RESET} ")?; + write!(buffer, "] {} ", STYLE_DIM.apply_to("="))?; } } pretty_format_value(buffer, &value, depth + 1)?; - write!(buffer, "{STYLE_DIM},{STYLE_RESET}")?; + write!(buffer, "{}", STYLE_DIM.apply_to(","))?; is_empty = false; } if is_empty { - write!(buffer, " {STYLE_DIM}}}{STYLE_RESET}")?; + write!(buffer, "{}", STYLE_DIM.apply_to(" }"))?; } else { - write!(buffer, "\n{depth_indent}{STYLE_DIM}}}{STYLE_RESET}")?; + write!(buffer, "\n{}", STYLE_DIM.apply_to("}"))?; } } } - LuaValue::Vector(x, y, z) => { - write!(buffer, "{COLOR_PURPLE}{COLOR_RESET}",)? - } - LuaValue::Thread(_) => write!(buffer, "{COLOR_PURPLE}{COLOR_RESET}")?, - LuaValue::Function(_) => write!(buffer, "{COLOR_PURPLE}{COLOR_RESET}")?, + LuaValue::Vector(x, y, z) => write!( + buffer, + "{}", + COLOR_PURPLE.apply_to(format!("")) + )?, + LuaValue::Thread(_) => write!(buffer, "{}", COLOR_PURPLE.apply_to(""))?, + LuaValue::Function(_) => write!(buffer, "{}", COLOR_PURPLE.apply_to(""))?, LuaValue::UserData(_) | LuaValue::LightUserData(_) => { - write!(buffer, "{COLOR_PURPLE}{COLOR_RESET}")? + write!(buffer, "{}", COLOR_PURPLE.apply_to(""))? } - _ => write!(buffer, "?")?, + _ => write!(buffer, "{}", STYLE_DIM.apply_to("?"))?, } Ok(()) } @@ -200,8 +191,8 @@ pub fn pretty_format_multi_value(multi: &LuaMultiValue) -> LuaResult { } pub fn pretty_format_luau_error(e: &LuaError) -> String { - let stack_begin = format!("[{}Stack Begin{}]", COLOR_BLUE, COLOR_RESET); - let stack_end = format!("[{}Stack End{}]", COLOR_BLUE, COLOR_RESET); + let stack_begin = format!("[{}]", COLOR_BLUE.apply_to("Stack Begin")); + let stack_end = format!("[{}]", COLOR_BLUE.apply_to("Stack End")); let err_string = match e { LuaError::RuntimeError(e) => { // Remove unnecessary prefix diff --git a/tests/console/set_style.luau b/tests/console/set_style.luau deleted file mode 100644 index bc8697d..0000000 --- a/tests/console/set_style.luau +++ /dev/null @@ -1,35 +0,0 @@ -local STYLES_VALID = { "bold", "dim" } -local STYLES_INVALID = { "", "*bold*", "dimm", "megabright", "cheerful", "sad", " " } - -local COLORS_VALID = { "black", "red", "green", "yellow", "blue", "purple", "cyan", "white" } -local COLORS_INVALID = { "", "gray", "grass", "red?", "super red", " ", "none" } - --- Test colors - -for _, color in COLORS_VALID do - if not pcall(console.setStyle, color :: any, nil) then - error(string.format("Setting color failed for color '%s'", color)) - end -end - -for _, color in COLORS_INVALID do - if pcall(console.setStyle, color :: any, nil) then - console.resetStyle() - error(string.format("Setting color should have failed for color '%s' but succeeded", color)) - end -end - --- Test styles - -for _, style in STYLES_VALID do - if not pcall(console.setStyle, nil, style :: any) then - error(string.format("Setting style failed for style '%s'", style)) - end -end - -for _, style in STYLES_INVALID do - if pcall(console.setStyle, nil, style :: any) then - console.resetStyle() - error(string.format("Setting style should have failed for style '%s' but succeeded", style)) - end -end diff --git a/tests/stdio/color.luau b/tests/stdio/color.luau new file mode 100644 index 0000000..bed92a6 --- /dev/null +++ b/tests/stdio/color.luau @@ -0,0 +1,15 @@ +local COLORS_VALID = + { "reset", "black", "red", "green", "yellow", "blue", "purple", "cyan", "white" } +local COLORS_INVALID = { "", "gray", "grass", "red?", "super red", " ", "none" } + +for _, color in COLORS_VALID do + stdio.color(color :: any) + stdio.color("reset") +end + +for _, color in COLORS_INVALID do + if pcall(stdio.color, color :: any) then + stdio.color("reset") + error(string.format("Setting color should have failed for color '%s' but succeeded", color)) + end +end diff --git a/tests/stdio/ewrite.luau b/tests/stdio/ewrite.luau new file mode 100644 index 0000000..92daa06 --- /dev/null +++ b/tests/stdio/ewrite.luau @@ -0,0 +1,3 @@ +stdio.ewrite("Hello, stderr!") + +process.exit(0) diff --git a/tests/console/format.luau b/tests/stdio/format.luau similarity index 60% rename from tests/console/format.luau rename to tests/stdio/format.luau index b5957b2..f52da01 100644 --- a/tests/console/format.luau +++ b/tests/stdio/format.luau @@ -1,10 +1,10 @@ assert( - console.format("Hello", "world", "!") == "Hello world !", + stdio.format("Hello", "world", "!") == "Hello world !", "Format should add a single space between arguments" ) assert( - console.format({ Hello = "World" }) == '{\n Hello = "World",\n}', + stdio.format({ Hello = "World" }) == '{\n Hello = "World",\n}', "Format should print out proper tables" ) @@ -21,6 +21,6 @@ local nested = { } assert( - string.find(console.format(nested), "Nesting = { ... }", 1, true) ~= nil, + string.find(stdio.format(nested), "Nesting = { ... }", 1, true) ~= nil, "Format should print 4 levels of nested tables before cutting off" ) diff --git a/tests/stdio/style.luau b/tests/stdio/style.luau new file mode 100644 index 0000000..00eed7f --- /dev/null +++ b/tests/stdio/style.luau @@ -0,0 +1,14 @@ +local STYLES_VALID = { "reset", "bold", "dim" } +local STYLES_INVALID = { "", "*bold*", "dimm", "megabright", "cheerful", "sad", " " } + +for _, style in STYLES_VALID do + stdio.style(style :: any) + stdio.style("reset") +end + +for _, style in STYLES_INVALID do + if pcall(stdio.style, style :: any) then + stdio.style("reset") + error(string.format("Setting style should have failed for style '%s' but succeeded", style)) + end +end diff --git a/tests/stdio/write.luau b/tests/stdio/write.luau new file mode 100644 index 0000000..b77c81e --- /dev/null +++ b/tests/stdio/write.luau @@ -0,0 +1,3 @@ +stdio.write("Hello, stdout!") + +process.exit(0) diff --git a/tests/task/fcheck.luau b/tests/task/fcheck.luau index a6e4139..08b6d6d 100644 --- a/tests/task/fcheck.luau +++ b/tests/task/fcheck.luau @@ -5,7 +5,7 @@ return function(index: number, type: string, value: any) "Expected argument #%d to be of type %s, got %s", index, type, - console.format(value) + stdio.format(value) ) ) end