Implement console global

This commit is contained in:
Filip Tibell 2023-01-19 22:10:34 -05:00
parent 9311edc7d8
commit aa1804d738
No known key found for this signature in database
8 changed files with 364 additions and 5 deletions

View file

@ -169,7 +169,36 @@ assert(apiResponse.body == "bar", "Invalid json response")
print("Got valid JSON response with changes applied")
--[==[
Example #8
EXAMPLE #8
Using the console library to print pretty
]==]
print("\nPrinting with pretty colors and auto-formatting 🎨")
console.setColor("blue")
print(string.rep("—", 22))
console.resetColor()
console.info("API response:", apiResponse)
console.warn({
Oh = {
No = {
TooMuch = {
Nesting = {
"Will not print",
},
},
},
},
})
console.setColor("blue")
print(string.rep("—", 22))
console.resetColor()
--[==[
EXAMPLE #9
Saying goodbye 😔
]==]

View file

@ -31,6 +31,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
assert(apiResponse.body == "bar", "Invalid json response")
```
- Added console logging & coloring functions under `console`
This piece of code:
```lua
local tab = { Integer = 1234, Hello = { "World" } }
console.log(tab)
```
Will print the following formatted text to the console, **_with syntax highlighting_**:
```lua
{
Integer = 1234,
Hello = {
"World",
}
}
```
Additional utility functions exist with the same behavior but that also print out a colored
tag together with any data given to them: `console.info`, `console.warn`, `console.error` -
These print out prefix tags `[INFO]`, `[WARN]`, `[ERROR]` in blue, orange, and red, respectively.
### Changed
- The `json` api is now part of `net`

View file

@ -32,7 +32,27 @@ Check out the examples of how to write a script in the [.lune](.lune) folder !
<details>
<summary><b>🔎 Full list of APIs</b></summary>
### **`fs`** - Filesystem
<details>
<summary><b>console</b> - Logging & formatting</summary>
```lua
type console = {
resetColor: () -> (),
setColor: (color: "black" | "red" | "green" | "yellow" | "blue" | "purple" | "cyan" | "white") -> (),
resetStyle: () -> (),
setStyle: (color: "bold" | "dim") -> (),
format: (...any) -> (string),
log: (...any) -> (),
info: (...any) -> (),
warn: (...any) -> (),
error: (...any) -> (),
}
```
</details>
<details>
<summary><b>fs</b> - Filesystem</summary>
```lua
type fs = {
@ -47,7 +67,10 @@ type fs = {
}
```
### **`net`** - Networking
</details>
<details>
<summary><b>net</b> - Networking</summary>
```lua
type net = {
@ -68,7 +91,10 @@ type net = {
}
```
### **`process`** - Current process & child processes
</details>
<details>
<summary><b>process</b> - Current process & child processes</summary>
```lua
type process = {
@ -87,6 +113,8 @@ type process = {
</details>
</details>
<details>
<summary><b>🔀 Example translation from Bash</b></summary>

View file

@ -1,6 +1,30 @@
# Lune v0.0.2
---
globals:
# Console
console.resetColor:
console.setColor:
args:
- type: string
console.resetStyle:
console.setStyle:
args:
- type: string
console.format:
args:
- type: "..."
console.log:
args:
- type: "..."
console.info:
args:
- type: "..."
console.warn:
args:
- type: "..."
console.error:
args:
- type: "..."
# FS (filesystem)
fs.readFile:
args:

View file

@ -1,5 +1,17 @@
-- Lune v0.0.2
declare console: {
resetColor: () -> (),
setColor: (color: "black" | "red" | "green" | "yellow" | "blue" | "purple" | "cyan" | "white") -> (),
resetStyle: () -> (),
setStyle: (color: "bold" | "dim") -> (),
format: (...any) -> (string),
log: (...any) -> (),
info: (...any) -> (),
warn: (...any) -> (),
error: (...any) -> (),
}
declare fs: {
readFile: (path: string) -> string,
readDir: (path: string) -> { string },

View file

@ -7,7 +7,7 @@ use clap::{CommandFactory, Parser};
use mlua::{Lua, MultiValue, Result, ToLua};
use crate::{
lune::{fs::LuneFs, net::LuneNet, process::LuneProcess},
lune::{console::LuneConsole, fs::LuneFs, net::LuneNet, process::LuneProcess},
utils::GithubClient,
};
@ -101,6 +101,7 @@ impl Cli {
// Create a new lua state and add in all lune globals
let lua = Lua::new();
let globals = lua.globals();
globals.set("console", LuneConsole::new())?;
globals.set("fs", LuneFs::new())?;
globals.set("net", LuneNet::new())?;
globals.set("process", LuneProcess::new())?;

240
src/lune/console.rs Normal file
View file

@ -0,0 +1,240 @@
use std::{
fmt::Write,
io::{self, Write as IoWrite},
};
use mlua::{Lua, MultiValue, Result, UserData, UserDataMethods, Value};
const MAX_FORMAT_DEPTH: usize = 4;
const INDENT: &str = " ";
const COLOR_RESET: &str = "\x1B[0m";
const COLOR_BLACK: &str = "\x1B[30m";
const COLOR_RED: &str = "\x1B[31m";
const COLOR_GREEN: &str = "\x1B[32m";
const COLOR_YELLOW: &str = "\x1B[33m";
const COLOR_BLUE: &str = "\x1B[34m";
const COLOR_PURPLE: &str = "\x1B[35m";
const COLOR_CYAN: &str = "\x1B[36m";
const COLOR_WHITE: &str = "\x1B[37m";
const STYLE_RESET: &str = "\x1B[22m";
const STYLE_BOLD: &str = "\x1B[1m";
const STYLE_DIM: &str = "\x1B[2m";
pub struct LuneConsole();
impl LuneConsole {
pub fn new() -> Self {
Self()
}
}
impl UserData for LuneConsole {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("resetColor", console_reset_color);
methods.add_function("setColor", console_set_color);
methods.add_function("resetStyle", console_reset_style);
methods.add_function("setStyle", console_set_style);
methods.add_function("format", console_format);
methods.add_function("log", console_log);
methods.add_function("info", console_info);
methods.add_function("warn", console_warn);
methods.add_function("error", console_error);
}
}
fn flush_stdout() -> Result<()> {
io::stdout().flush().map_err(mlua::Error::external)
}
fn can_be_plain_lua_table_key(s: &mlua::String) -> bool {
let str = s.to_string_lossy().to_string();
let first_char = str.chars().next().unwrap();
if first_char.is_alphabetic() {
str.chars().all(|c| c == '_' || c.is_alphanumeric())
} else {
false
}
}
fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) -> anyhow::Result<()> {
// TODO: Handle tables with cyclic references
// TODO: Handle other types like function, userdata, ...
match &value {
Value::Nil => write!(buffer, "nil")?,
Value::Boolean(true) => write!(buffer, "{}true{}", COLOR_YELLOW, COLOR_RESET)?,
Value::Boolean(false) => write!(buffer, "{}false{}", COLOR_YELLOW, COLOR_RESET)?,
Value::Number(n) => write!(buffer, "{}{}{}", COLOR_BLUE, n, COLOR_RESET)?,
Value::Integer(i) => write!(buffer, "{}{}{}", COLOR_BLUE, i, COLOR_RESET)?,
Value::String(s) => write!(
buffer,
"{}\"{}\"{}",
COLOR_GREEN,
s.to_string_lossy()
.replace('"', r#"\""#)
.replace('\n', r#"\n"#),
COLOR_RESET
)?,
Value::Table(ref tab) => {
if depth >= MAX_FORMAT_DEPTH {
write!(buffer, "{}{{ ... }}{}", STYLE_DIM, STYLE_RESET)?;
} else {
let depth_indent = INDENT.repeat(depth);
write!(buffer, "{}{{{}", STYLE_DIM, STYLE_RESET)?;
for pair in tab.clone().pairs::<Value, Value>() {
let (key, value) = pair?;
match &key {
Value::String(s) if can_be_plain_lua_table_key(s) => write!(
buffer,
"\n{}{}{} {}={} ",
depth_indent,
INDENT,
s.to_string_lossy(),
STYLE_DIM,
STYLE_RESET
)?,
_ => {
write!(buffer, "\n{}{}[", depth_indent, INDENT)?;
pretty_format_value(buffer, &key, depth)?;
write!(buffer, "] {}={} ", STYLE_DIM, STYLE_RESET)?;
}
}
pretty_format_value(buffer, &value, depth + 1)?;
write!(buffer, "{},{}", STYLE_DIM, STYLE_RESET)?;
}
write!(buffer, "\n{}{}}}{}", depth_indent, STYLE_DIM, STYLE_RESET)?;
}
}
_ => write!(buffer, "?")?,
}
Ok(())
}
fn pretty_format_multi_value(multi: &MultiValue) -> Result<String> {
let mut buffer = String::new();
let mut counter = 0;
for value in multi {
counter += 1;
if let Value::String(s) = value {
write!(buffer, "{}", s.to_string_lossy()).map_err(mlua::Error::external)?
} else {
pretty_format_value(&mut buffer, value, 0).map_err(mlua::Error::external)?;
}
if counter < multi.len() {
write!(&mut buffer, " ").map_err(mlua::Error::external)?;
}
}
Ok(buffer)
}
fn print_style<S: AsRef<str>>(s: S) -> Result<()> {
print!(
"{}",
match s.as_ref() {
"reset" => STYLE_RESET,
"bold" => STYLE_BOLD,
"dim" => STYLE_DIM,
_ => {
return Err(mlua::Error::RuntimeError(format!(
"The style '{}' is not a valid style name",
s.as_ref()
)));
}
}
);
flush_stdout()?;
Ok(())
}
fn print_color<S: AsRef<str>>(s: S) -> Result<()> {
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(mlua::Error::RuntimeError(format!(
"The color '{}' is not a valid color name",
s.as_ref()
)));
}
}
);
flush_stdout()?;
Ok(())
}
fn console_reset_color(_: &Lua, _: ()) -> Result<()> {
print_color("reset")?;
flush_stdout()?;
Ok(())
}
fn console_set_color(_: &Lua, color: String) -> Result<()> {
print_color(color.trim().to_ascii_lowercase())?;
Ok(())
}
fn console_reset_style(_: &Lua, _: ()) -> Result<()> {
print_style("reset")?;
flush_stdout()?;
Ok(())
}
fn console_set_style(_: &Lua, style: String) -> Result<()> {
print_style(style.trim().to_ascii_lowercase())?;
Ok(())
}
fn console_format(_: &Lua, args: MultiValue) -> Result<String> {
pretty_format_multi_value(&args)
}
fn console_log(_: &Lua, args: MultiValue) -> Result<()> {
let s = pretty_format_multi_value(&args)?;
println!("{}", s);
flush_stdout()?;
Ok(())
}
fn console_info(_: &Lua, args: MultiValue) -> Result<()> {
print!(
"{}{}[INFO]{}{} ",
STYLE_BOLD, COLOR_CYAN, COLOR_RESET, STYLE_RESET
);
let s = pretty_format_multi_value(&args)?;
println!("{}", s);
flush_stdout()?;
Ok(())
}
fn console_warn(_: &Lua, args: MultiValue) -> Result<()> {
print!(
"{}{}[WARN]{}{} ",
STYLE_BOLD, COLOR_YELLOW, COLOR_RESET, STYLE_RESET
);
let s = pretty_format_multi_value(&args)?;
println!("{}", s);
flush_stdout()?;
Ok(())
}
fn console_error(_: &Lua, args: MultiValue) -> Result<()> {
eprint!(
"{}{}[ERROR]{}{} ",
STYLE_BOLD, COLOR_RED, COLOR_RESET, STYLE_RESET
);
let s = pretty_format_multi_value(&args)?;
eprintln!("{}", s);
flush_stdout()?;
Ok(())
}

View file

@ -1,3 +1,4 @@
pub mod console;
pub mod fs;
pub mod net;
pub mod process;