diff --git a/Cargo.toml b/Cargo.toml index e60f940..8891120 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,11 @@ categories = ["command-line-interface"] [[bin]] name = "lune" -path = "src/main.rs" +path = "src/cli/main.rs" + +[lib] +name = "lune" +path = "src/lib/lib.rs" [profile.release] strip = true # Automatically strip symbols from the binary. diff --git a/src/cli.rs b/src/cli/cli.rs similarity index 60% rename from src/cli.rs rename to src/cli/cli.rs index 0e1f877..e93a991 100644 --- a/src/cli.rs +++ b/src/cli/cli.rs @@ -1,15 +1,11 @@ -use std::{ - fs::read_to_string, - path::{PathBuf, MAIN_SEPARATOR}, -}; +use std::fs::read_to_string; +use anyhow::Result; use clap::{CommandFactory, Parser}; -use mlua::{Lua, Result}; -use crate::{ - globals::{console::Console, fs::Fs, net::Net, process::Process}, - utils::github::Client as GithubClient, -}; +use lune::Lune; + +use crate::utils::{files::find_parse_file_path, github::Client as GithubClient}; /// Lune CLI #[derive(Parser, Debug, Default)] @@ -97,62 +93,14 @@ impl Cli { // Parse and read the wanted file let file_path = find_parse_file_path(&self.script_path.unwrap())?; let file_contents = read_to_string(&file_path)?; - // Create a new lua state and add in all lune globals - let lua = Lua::new(); - let globals = lua.globals(); - globals.set("console", Console::new())?; - globals.set("fs", Fs::new())?; - globals.set("net", Net::new())?; - globals.set("process", Process::new(self.script_args))?; - lua.sandbox(true)?; - // Load & call the file with the given args - lua.load(&file_contents) - .set_name(file_path.with_extension("").display().to_string())? - .exec_async() + // Display the file path relative to cwd with no extensions in stack traces + let file_display_name = file_path.with_extension("").display().to_string(); + // Create a new lune object with all globals & run the script + Lune::new()? + .with_args(self.script_args)? + .with_default_globals()? + .run_with_name(&file_contents, &file_display_name) .await?; Ok(()) } } - -fn find_luau_file_path(path: &str) -> Option { - let file_path = PathBuf::from(path); - if let Some(ext) = file_path.extension() { - match ext { - e if e == "lua" || e == "luau" && file_path.exists() => Some(file_path), - _ => None, - } - } else { - let file_path_lua = PathBuf::from(path).with_extension("lua"); - if file_path_lua.exists() { - Some(file_path_lua) - } else { - let file_path_luau = PathBuf::from(path).with_extension("luau"); - if file_path_luau.exists() { - Some(file_path_luau) - } else { - None - } - } - } -} - -fn find_parse_file_path(path: &str) -> Result { - let parsed_file_path = find_luau_file_path(path) - .or_else(|| find_luau_file_path(&format!("lune{MAIN_SEPARATOR}{path}"))) - .or_else(|| find_luau_file_path(&format!(".lune{MAIN_SEPARATOR}{path}"))); - if let Some(file_path) = parsed_file_path { - if file_path.exists() { - Ok(file_path) - } else { - Err(mlua::Error::RuntimeError(format!( - "File does not exist at path: '{}'", - path - ))) - } - } else { - Err(mlua::Error::RuntimeError(format!( - "Invalid file path: '{}'", - path - ))) - } -} diff --git a/src/main.rs b/src/cli/main.rs similarity index 57% rename from src/main.rs rename to src/cli/main.rs index 9227d1e..53f83c8 100644 --- a/src/main.rs +++ b/src/cli/main.rs @@ -1,29 +1,19 @@ #![deny(clippy::all, clippy::cargo, clippy::pedantic)] #![allow(clippy::needless_pass_by_value, clippy::match_bool)] +use anyhow::Result; use clap::Parser; -use mlua::Result; mod cli; -mod globals; mod utils; use cli::Cli; -use utils::formatting::{pretty_print_luau_error, print_label}; #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); - match cli.run().await { - Ok(_) => Ok(()), - Err(e) => { - eprintln!(); - print_label("ERROR").unwrap(); - eprintln!(); - pretty_print_luau_error(&e); - std::process::exit(1); - } - } + cli.run().await?; + Ok(()) } #[tokio::test] diff --git a/src/cli/utils/files.rs b/src/cli/utils/files.rs new file mode 100644 index 0000000..c315eb0 --- /dev/null +++ b/src/cli/utils/files.rs @@ -0,0 +1,40 @@ +use std::path::{PathBuf, MAIN_SEPARATOR}; + +use anyhow::{bail, Result}; + +pub fn find_luau_file_path(path: &str) -> Option { + let file_path = PathBuf::from(path); + if let Some(ext) = file_path.extension() { + match ext { + e if e == "lua" || e == "luau" && file_path.exists() => Some(file_path), + _ => None, + } + } else { + let file_path_lua = PathBuf::from(path).with_extension("lua"); + if file_path_lua.exists() { + Some(file_path_lua) + } else { + let file_path_luau = PathBuf::from(path).with_extension("luau"); + if file_path_luau.exists() { + Some(file_path_luau) + } else { + None + } + } + } +} + +pub fn find_parse_file_path(path: &str) -> Result { + let parsed_file_path = find_luau_file_path(path) + .or_else(|| find_luau_file_path(&format!("lune{MAIN_SEPARATOR}{path}"))) + .or_else(|| find_luau_file_path(&format!(".lune{MAIN_SEPARATOR}{path}"))); + if let Some(file_path) = parsed_file_path { + if file_path.exists() { + Ok(file_path) + } else { + bail!("File does not exist at path: '{}'", path) + } + } else { + bail!("Invalid file path: '{}'", path) + } +} diff --git a/src/utils/github.rs b/src/cli/utils/github.rs similarity index 87% rename from src/utils/github.rs rename to src/cli/utils/github.rs index 4c9f47c..f073865 100644 --- a/src/utils/github.rs +++ b/src/cli/utils/github.rs @@ -4,6 +4,8 @@ use anyhow::{bail, Context, Result}; use reqwest::header::{HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; +use lune::utils::net::{get_github_owner_and_repo, get_request_user_agent_header}; + #[derive(Clone, Deserialize, Serialize)] pub struct ReleaseAsset { id: u64, @@ -38,7 +40,7 @@ impl Client { let mut headers = HeaderMap::new(); headers.insert( "User-Agent", - HeaderValue::from_str(&get_github_user_agent_header())?, + HeaderValue::from_str(&get_request_user_agent_header())?, ); headers.insert( "Accept", @@ -118,17 +120,3 @@ impl Client { Ok(()) } } - -pub fn get_github_owner_and_repo() -> (String, String) { - let (github_owner, github_repo) = env!("CARGO_PKG_REPOSITORY") - .strip_prefix("https://github.com/") - .unwrap() - .split_once('/') - .unwrap(); - (github_owner.to_owned(), github_repo.to_owned()) -} - -pub fn get_github_user_agent_header() -> String { - let (github_owner, github_repo) = get_github_owner_and_repo(); - format!("{github_owner}-{github_repo}-cli") -} diff --git a/src/cli/utils/mod.rs b/src/cli/utils/mod.rs new file mode 100644 index 0000000..557c0bd --- /dev/null +++ b/src/cli/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod files; +pub mod github; diff --git a/src/globals/mod.rs b/src/globals/mod.rs deleted file mode 100644 index 79f3e23..0000000 --- a/src/globals/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod console; -pub mod fs; -pub mod net; -pub mod process; diff --git a/src/globals/console.rs b/src/lib/globals/console.rs similarity index 96% rename from src/globals/console.rs rename to src/lib/globals/console.rs index 00828bd..514d2ec 100644 --- a/src/globals/console.rs +++ b/src/lib/globals/console.rs @@ -12,6 +12,12 @@ impl Console { } } +impl Default for Console { + fn default() -> Self { + Self::new() + } +} + impl UserData for Console { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_function("resetColor", console_reset_color); diff --git a/src/globals/fs.rs b/src/lib/globals/fs.rs similarity index 97% rename from src/globals/fs.rs rename to src/lib/globals/fs.rs index 0c33a3c..930dbdf 100644 --- a/src/globals/fs.rs +++ b/src/lib/globals/fs.rs @@ -11,6 +11,12 @@ impl Fs { } } +impl Default for Fs { + fn default() -> Self { + Self::new() + } +} + impl UserData for Fs { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_async_function("readFile", fs_read_file); diff --git a/src/lib/globals/mod.rs b/src/lib/globals/mod.rs new file mode 100644 index 0000000..503ea43 --- /dev/null +++ b/src/lib/globals/mod.rs @@ -0,0 +1,9 @@ +mod console; +mod fs; +mod net; +mod process; + +pub use console::Console; +pub use fs::Fs; +pub use net::Net; +pub use process::Process; diff --git a/src/globals/net.rs b/src/lib/globals/net.rs similarity index 95% rename from src/globals/net.rs rename to src/lib/globals/net.rs index f624d26..7aaa385 100644 --- a/src/globals/net.rs +++ b/src/lib/globals/net.rs @@ -6,7 +6,7 @@ use reqwest::{ Method, }; -use crate::utils::github::get_github_user_agent_header; +use crate::utils::net::get_request_user_agent_header; pub struct Net(); @@ -16,6 +16,12 @@ impl Net { } } +impl Default for Net { + fn default() -> Self { + Self::new() + } +} + impl UserData for Net { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_function("jsonEncode", net_json_encode); @@ -98,7 +104,7 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result, } +impl Default for Process { + fn default() -> Self { + Self::new(vec![]) + } +} + impl Process { pub fn new(args: Vec) -> Self { Self { args } diff --git a/src/lib/lib.rs b/src/lib/lib.rs new file mode 100644 index 0000000..81f547b --- /dev/null +++ b/src/lib/lib.rs @@ -0,0 +1,60 @@ +use anyhow::Result; +use mlua::Lua; + +pub mod globals; +pub mod utils; + +use crate::{ + globals::{Console, Fs, Net, Process}, + utils::formatting::{pretty_print_luau_error, print_label}, +}; + +pub struct Lune { + lua: Lua, + args: Vec, +} + +impl Lune { + pub fn new() -> Result { + let lua = Lua::new(); + lua.sandbox(true)?; + Ok(Self { lua, args: vec![] }) + } + + pub fn with_args(mut self, args: Vec) -> Result { + self.args = args; + Ok(self) + } + + pub fn with_default_globals(self) -> Result { + { + let globals = self.lua.globals(); + globals.set("console", Console::new())?; + globals.set("fs", Fs())?; + globals.set("net", Net::new())?; + globals.set("process", Process::new(self.args.clone()))?; + } + Ok(self) + } + + pub async fn run(&self, chunk: &str) -> Result<()> { + self.handle_result(self.lua.load(chunk).exec_async().await) + } + + pub async fn run_with_name(&self, chunk: &str, name: &str) -> Result<()> { + self.handle_result(self.lua.load(chunk).set_name(name)?.exec_async().await) + } + + fn handle_result(&self, result: mlua::Result<()>) -> Result<()> { + match result { + Ok(_) => Ok(()), + Err(e) => { + eprintln!(); + print_label("ERROR").unwrap(); + eprintln!(); + pretty_print_luau_error(&e); + std::process::exit(1); + } + } + } +} diff --git a/src/utils/formatting.rs b/src/lib/utils/formatting.rs similarity index 93% rename from src/utils/formatting.rs rename to src/lib/utils/formatting.rs index 1c33ad6..ad01fe1 100644 --- a/src/utils/formatting.rs +++ b/src/lib/utils/formatting.rs @@ -107,8 +107,8 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) -> Value::Nil => write!(buffer, "nil")?, Value::Boolean(true) => write!(buffer, "{COLOR_YELLOW}true{COLOR_RESET}")?, Value::Boolean(false) => write!(buffer, "{COLOR_YELLOW}false{COLOR_RESET}")?, - Value::Number(n) => write!(buffer, "{COLOR_BLUE}{n}{COLOR_RESET}")?, - Value::Integer(i) => write!(buffer, "{COLOR_BLUE}{i}{COLOR_RESET}")?, + Value::Number(n) => write!(buffer, "{COLOR_CYAN}{n}{COLOR_RESET}")?, + Value::Integer(i) => write!(buffer, "{COLOR_CYAN}{i}{COLOR_RESET}")?, Value::String(s) => write!( buffer, "{}\"{}\"{}", @@ -122,6 +122,7 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) -> if depth >= MAX_FORMAT_DEPTH { write!(buffer, "{STYLE_DIM}{{ ... }}{STYLE_RESET}")?; } else { + let mut is_empty = false; let depth_indent = INDENT.repeat(depth); write!(buffer, "{STYLE_DIM}{{{STYLE_RESET}")?; for pair in tab.clone().pairs::() { @@ -144,8 +145,13 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) -> } pretty_format_value(buffer, &value, depth + 1)?; write!(buffer, "{STYLE_DIM},{STYLE_RESET}")?; + is_empty = false; + } + if is_empty { + write!(buffer, " {STYLE_DIM}}}{STYLE_RESET}")?; + } else { + write!(buffer, "\n{depth_indent}{STYLE_DIM}}}{STYLE_RESET}")?; } - write!(buffer, "\n{depth_indent}{STYLE_DIM}}}{STYLE_RESET}")?; } } _ => write!(buffer, "?")?, diff --git a/src/utils/mod.rs b/src/lib/utils/mod.rs similarity index 55% rename from src/utils/mod.rs rename to src/lib/utils/mod.rs index 37ce64f..5168df1 100644 --- a/src/utils/mod.rs +++ b/src/lib/utils/mod.rs @@ -1,2 +1,2 @@ pub mod formatting; -pub mod github; +pub mod net; diff --git a/src/lib/utils/net.rs b/src/lib/utils/net.rs new file mode 100644 index 0000000..9625650 --- /dev/null +++ b/src/lib/utils/net.rs @@ -0,0 +1,13 @@ +pub fn get_github_owner_and_repo() -> (String, String) { + let (github_owner, github_repo) = env!("CARGO_PKG_REPOSITORY") + .strip_prefix("https://github.com/") + .unwrap() + .split_once('/') + .unwrap(); + (github_owner.to_owned(), github_repo.to_owned()) +} + +pub fn get_request_user_agent_header() -> String { + let (github_owner, github_repo) = get_github_owner_and_repo(); + format!("{github_owner}-{github_repo}-cli") +}