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]]
|
||||
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.
|
||||
|
|
|
@ -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<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)]
|
||||
#![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]
|
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 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")
|
||||
}
|
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 {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
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 {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
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,
|
||||
};
|
||||
|
||||
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<Value<
|
|||
};
|
||||
header_map.insert(
|
||||
"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
|
||||
// FUTURE: Try to reuse this client
|
|
@ -14,6 +14,12 @@ pub struct Process {
|
|||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for Process {
|
||||
fn default() -> Self {
|
||||
Self::new(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn new(args: Vec<String>) -> Self {
|
||||
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::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::<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)?;
|
||||
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, "?")?,
|
|
@ -1,2 +1,2 @@
|
|||
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