mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Split into lune lib & lune cli for easier external usage
This commit is contained in:
parent
2565e02e11
commit
c90f40cf30
16 changed files with 183 additions and 103 deletions
|
@ -10,7 +10,11 @@ categories = ["command-line-interface"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "lune"
|
name = "lune"
|
||||||
path = "src/main.rs"
|
path = "src/cli/main.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "lune"
|
||||||
|
path = "src/lib/lib.rs"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true # Automatically strip symbols from the binary.
|
strip = true # Automatically strip symbols from the binary.
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
use std::{
|
use std::fs::read_to_string;
|
||||||
fs::read_to_string,
|
|
||||||
path::{PathBuf, MAIN_SEPARATOR},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
use mlua::{Lua, Result};
|
|
||||||
|
|
||||||
use crate::{
|
use lune::Lune;
|
||||||
globals::{console::Console, fs::Fs, net::Net, process::Process},
|
|
||||||
utils::github::Client as GithubClient,
|
use crate::utils::{files::find_parse_file_path, github::Client as GithubClient};
|
||||||
};
|
|
||||||
|
|
||||||
/// Lune CLI
|
/// Lune CLI
|
||||||
#[derive(Parser, Debug, Default)]
|
#[derive(Parser, Debug, Default)]
|
||||||
|
@ -97,62 +93,14 @@ impl Cli {
|
||||||
// Parse and read the wanted file
|
// Parse and read the wanted file
|
||||||
let file_path = find_parse_file_path(&self.script_path.unwrap())?;
|
let file_path = find_parse_file_path(&self.script_path.unwrap())?;
|
||||||
let file_contents = read_to_string(&file_path)?;
|
let file_contents = read_to_string(&file_path)?;
|
||||||
// Create a new lua state and add in all lune globals
|
// Display the file path relative to cwd with no extensions in stack traces
|
||||||
let lua = Lua::new();
|
let file_display_name = file_path.with_extension("").display().to_string();
|
||||||
let globals = lua.globals();
|
// Create a new lune object with all globals & run the script
|
||||||
globals.set("console", Console::new())?;
|
Lune::new()?
|
||||||
globals.set("fs", Fs::new())?;
|
.with_args(self.script_args)?
|
||||||
globals.set("net", Net::new())?;
|
.with_default_globals()?
|
||||||
globals.set("process", Process::new(self.script_args))?;
|
.run_with_name(&file_contents, &file_display_name)
|
||||||
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()
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_luau_file_path(path: &str) -> Option<PathBuf> {
|
|
||||||
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<PathBuf> {
|
|
||||||
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
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +1,19 @@
|
||||||
#![deny(clippy::all, clippy::cargo, clippy::pedantic)]
|
#![deny(clippy::all, clippy::cargo, clippy::pedantic)]
|
||||||
#![allow(clippy::needless_pass_by_value, clippy::match_bool)]
|
#![allow(clippy::needless_pass_by_value, clippy::match_bool)]
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mlua::Result;
|
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod globals;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use utils::formatting::{pretty_print_luau_error, print_label};
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
match cli.run().await {
|
cli.run().await?;
|
||||||
Ok(_) => Ok(()),
|
Ok(())
|
||||||
Err(e) => {
|
|
||||||
eprintln!();
|
|
||||||
print_label("ERROR").unwrap();
|
|
||||||
eprintln!();
|
|
||||||
pretty_print_luau_error(&e);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
40
src/cli/utils/files.rs
Normal file
40
src/cli/utils/files.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
pub fn find_luau_file_path(path: &str) -> Option<PathBuf> {
|
||||||
|
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<PathBuf> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ use anyhow::{bail, Context, Result};
|
||||||
use reqwest::header::{HeaderMap, HeaderValue};
|
use reqwest::header::{HeaderMap, HeaderValue};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use lune::utils::net::{get_github_owner_and_repo, get_request_user_agent_header};
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
pub struct ReleaseAsset {
|
pub struct ReleaseAsset {
|
||||||
id: u64,
|
id: u64,
|
||||||
|
@ -38,7 +40,7 @@ impl Client {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
HeaderValue::from_str(&get_github_user_agent_header())?,
|
HeaderValue::from_str(&get_request_user_agent_header())?,
|
||||||
);
|
);
|
||||||
headers.insert(
|
headers.insert(
|
||||||
"Accept",
|
"Accept",
|
||||||
|
@ -118,17 +120,3 @@ impl Client {
|
||||||
Ok(())
|
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")
|
|
||||||
}
|
|
2
src/cli/utils/mod.rs
Normal file
2
src/cli/utils/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod files;
|
||||||
|
pub mod github;
|
|
@ -1,4 +0,0 @@
|
||||||
pub mod console;
|
|
||||||
pub mod fs;
|
|
||||||
pub mod net;
|
|
||||||
pub mod process;
|
|
|
@ -12,6 +12,12 @@ impl Console {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Console {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserData for Console {
|
impl UserData for Console {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_function("resetColor", console_reset_color);
|
methods.add_function("resetColor", console_reset_color);
|
|
@ -11,6 +11,12 @@ impl Fs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Fs {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserData for Fs {
|
impl UserData for Fs {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_function("readFile", fs_read_file);
|
methods.add_async_function("readFile", fs_read_file);
|
9
src/lib/globals/mod.rs
Normal file
9
src/lib/globals/mod.rs
Normal file
|
@ -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;
|
|
@ -6,7 +6,7 @@ use reqwest::{
|
||||||
Method,
|
Method,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::utils::github::get_github_user_agent_header;
|
use crate::utils::net::get_request_user_agent_header;
|
||||||
|
|
||||||
pub struct Net();
|
pub struct Net();
|
||||||
|
|
||||||
|
@ -16,6 +16,12 @@ impl Net {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Net {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserData for Net {
|
impl UserData for Net {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_function("jsonEncode", net_json_encode);
|
methods.add_function("jsonEncode", net_json_encode);
|
||||||
|
@ -98,7 +104,7 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Value<
|
||||||
};
|
};
|
||||||
header_map.insert(
|
header_map.insert(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
HeaderValue::from_str(&get_github_user_agent_header()).map_err(Error::external)?,
|
HeaderValue::from_str(&get_request_user_agent_header()).map_err(Error::external)?,
|
||||||
);
|
);
|
||||||
// Create a client to send a request with
|
// Create a client to send a request with
|
||||||
// FUTURE: Try to reuse this client
|
// FUTURE: Try to reuse this client
|
|
@ -14,6 +14,12 @@ pub struct Process {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Process {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn new(args: Vec<String>) -> Self {
|
pub fn new(args: Vec<String>) -> Self {
|
||||||
Self { args }
|
Self { args }
|
60
src/lib/lib.rs
Normal file
60
src/lib/lib.rs
Normal file
|
@ -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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lune {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.sandbox(true)?;
|
||||||
|
Ok(Self { lua, args: vec![] })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_args(mut self, args: Vec<String>) -> Result<Self> {
|
||||||
|
self.args = args;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_default_globals(self) -> Result<Self> {
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,8 +107,8 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) ->
|
||||||
Value::Nil => write!(buffer, "nil")?,
|
Value::Nil => write!(buffer, "nil")?,
|
||||||
Value::Boolean(true) => write!(buffer, "{COLOR_YELLOW}true{COLOR_RESET}")?,
|
Value::Boolean(true) => write!(buffer, "{COLOR_YELLOW}true{COLOR_RESET}")?,
|
||||||
Value::Boolean(false) => write!(buffer, "{COLOR_YELLOW}false{COLOR_RESET}")?,
|
Value::Boolean(false) => write!(buffer, "{COLOR_YELLOW}false{COLOR_RESET}")?,
|
||||||
Value::Number(n) => write!(buffer, "{COLOR_BLUE}{n}{COLOR_RESET}")?,
|
Value::Number(n) => write!(buffer, "{COLOR_CYAN}{n}{COLOR_RESET}")?,
|
||||||
Value::Integer(i) => write!(buffer, "{COLOR_BLUE}{i}{COLOR_RESET}")?,
|
Value::Integer(i) => write!(buffer, "{COLOR_CYAN}{i}{COLOR_RESET}")?,
|
||||||
Value::String(s) => write!(
|
Value::String(s) => write!(
|
||||||
buffer,
|
buffer,
|
||||||
"{}\"{}\"{}",
|
"{}\"{}\"{}",
|
||||||
|
@ -122,6 +122,7 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) ->
|
||||||
if depth >= MAX_FORMAT_DEPTH {
|
if depth >= MAX_FORMAT_DEPTH {
|
||||||
write!(buffer, "{STYLE_DIM}{{ ... }}{STYLE_RESET}")?;
|
write!(buffer, "{STYLE_DIM}{{ ... }}{STYLE_RESET}")?;
|
||||||
} else {
|
} else {
|
||||||
|
let mut is_empty = false;
|
||||||
let depth_indent = INDENT.repeat(depth);
|
let depth_indent = INDENT.repeat(depth);
|
||||||
write!(buffer, "{STYLE_DIM}{{{STYLE_RESET}")?;
|
write!(buffer, "{STYLE_DIM}{{{STYLE_RESET}")?;
|
||||||
for pair in tab.clone().pairs::<Value, Value>() {
|
for pair in tab.clone().pairs::<Value, Value>() {
|
||||||
|
@ -144,8 +145,13 @@ pub fn pretty_format_value(buffer: &mut String, value: &Value, depth: usize) ->
|
||||||
}
|
}
|
||||||
pretty_format_value(buffer, &value, depth + 1)?;
|
pretty_format_value(buffer, &value, depth + 1)?;
|
||||||
write!(buffer, "{STYLE_DIM},{STYLE_RESET}")?;
|
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, "?")?,
|
_ => write!(buffer, "?")?,
|
|
@ -1,2 +1,2 @@
|
||||||
pub mod formatting;
|
pub mod formatting;
|
||||||
pub mod github;
|
pub mod net;
|
13
src/lib/utils/net.rs
Normal file
13
src/lib/utils/net.rs
Normal file
|
@ -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")
|
||||||
|
}
|
Loading…
Reference in a new issue