mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Migrate away from tokio & reqwest, use smol & ureq
This commit is contained in:
parent
e5e96dfd54
commit
06339a2699
10 changed files with 394 additions and 717 deletions
811
Cargo.lock
generated
811
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
@ -23,12 +23,12 @@ lto = true # Enable link-time optimization
|
|||
panic = "abort" # Remove extra panic info
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.68" }
|
||||
anyhow = "1.0.68"
|
||||
clap = { version = "4.1.1", features = ["derive"] }
|
||||
mlua = { version = "0.8.7", features = ["luau", "async", "serialize"] }
|
||||
once_cell = { version = "1.17.0" }
|
||||
os_str_bytes = { version = "6.4.1" }
|
||||
reqwest = { version = "0.11.13", features = ["gzip", "deflate"] }
|
||||
once_cell = "1.17.0"
|
||||
os_str_bytes = "6.4.1"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.91" }
|
||||
tokio = { version = "1.24.2", features = ["full"] }
|
||||
serde_json = "1.0.91"
|
||||
smol = "1.3.0"
|
||||
ureq = "2.6.2"
|
||||
|
|
|
@ -57,7 +57,7 @@ impl Cli {
|
|||
// Download definition files, if wanted
|
||||
let download_types_requested = self.download_selene_types || self.download_luau_types;
|
||||
if download_types_requested {
|
||||
let client = GithubClient::new().map_err(mlua::Error::external)?;
|
||||
let client = GithubClient::new();
|
||||
let release = client
|
||||
.fetch_release_for_this_version()
|
||||
.await
|
||||
|
|
|
@ -10,9 +10,10 @@ mod utils;
|
|||
|
||||
use cli::Cli;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
cli.run().await?;
|
||||
Ok(())
|
||||
fn main() -> Result<()> {
|
||||
smol::block_on(async {
|
||||
let cli = Cli::parse();
|
||||
cli.run().await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::env::current_dir;
|
||||
|
||||
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};
|
||||
use smol::unblock;
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ReleaseAsset {
|
||||
|
@ -29,35 +29,28 @@ pub struct Release {
|
|||
}
|
||||
|
||||
pub struct Client {
|
||||
client: reqwest::Client,
|
||||
github_owner: String,
|
||||
github_repo: String,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn new() -> Self {
|
||||
let (github_owner, github_repo) = get_github_owner_and_repo();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
"User-Agent",
|
||||
HeaderValue::from_str(&get_request_user_agent_header())?,
|
||||
);
|
||||
headers.insert(
|
||||
"Accept",
|
||||
HeaderValue::from_static("application/vnd.github+json"),
|
||||
);
|
||||
headers.insert(
|
||||
"X-GitHub-Api-Version",
|
||||
HeaderValue::from_static("2022-11-28"),
|
||||
);
|
||||
let client = reqwest::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()?;
|
||||
Ok(Self {
|
||||
client,
|
||||
Self {
|
||||
github_owner,
|
||||
github_repo,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get(&self, url: &str, headers: &[(&str, &str)]) -> Result<String> {
|
||||
let mut request = ureq::get(url)
|
||||
.set("User-Agent", &get_request_user_agent_header())
|
||||
.set("Accept", "application/vnd.github+json")
|
||||
.set("X-GitHub-Api-Version", "2022-11-28");
|
||||
for (header, value) in headers {
|
||||
request = request.set(header, value);
|
||||
}
|
||||
unblock(|| Ok(request.send_string("")?.into_string()?)).await
|
||||
}
|
||||
|
||||
pub async fn fetch_releases(&self) -> Result<Vec<Release>> {
|
||||
|
@ -65,17 +58,8 @@ impl Client {
|
|||
"https://api.github.com/repos/{}/{}/releases",
|
||||
&self.github_owner, &self.github_repo
|
||||
);
|
||||
let response_bytes = self
|
||||
.client
|
||||
.get(release_api_url)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send releases request")?
|
||||
.bytes()
|
||||
.await
|
||||
.context("Failed to get releases response bytes")?;
|
||||
let response_body: Vec<Release> = serde_json::from_slice(&response_bytes)?;
|
||||
Ok(response_body)
|
||||
let response_string = self.get(&release_api_url, &[]).await?;
|
||||
Ok(serde_json::from_str(&response_string)?)
|
||||
}
|
||||
|
||||
pub async fn fetch_release_for_this_version(&self) -> Result<Release> {
|
||||
|
@ -95,17 +79,8 @@ impl Client {
|
|||
.find(|asset| matches!(&asset.name, Some(name) if name == asset_name))
|
||||
{
|
||||
let file_path = current_dir()?.join(asset_name);
|
||||
let file_bytes = self
|
||||
.client
|
||||
.get(&asset.url)
|
||||
.header("Accept", "application/octet-stream")
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send asset download request")?
|
||||
.bytes()
|
||||
.await
|
||||
.context("Failed to get asset download response bytes")?;
|
||||
tokio::fs::write(&file_path, &file_bytes)
|
||||
let file_string = self.get(&asset.url, &[]).await?;
|
||||
smol::fs::write(&file_path, &file_string)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Failed to write file at path '{}'", &file_path.display())
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
use tokio::fs;
|
||||
use smol::{fs, prelude::*};
|
||||
|
||||
use crate::utils::table_builder::TableBuilder;
|
||||
|
||||
|
@ -30,7 +30,7 @@ async fn fs_read_file(_: &Lua, path: String) -> Result<String> {
|
|||
async fn fs_read_dir(_: &Lua, path: String) -> Result<Vec<String>> {
|
||||
let mut dir_strings = Vec::new();
|
||||
let mut dir = fs::read_dir(&path).await.map_err(mlua::Error::external)?;
|
||||
while let Some(dir_entry) = dir.next_entry().await.map_err(mlua::Error::external)? {
|
||||
while let Some(dir_entry) = dir.try_next().await.map_err(mlua::Error::external)? {
|
||||
if let Some(dir_path_str) = dir_entry.path().to_str() {
|
||||
dir_strings.push(dir_path_str.to_owned());
|
||||
} else {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result, Table, Value};
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderName, HeaderValue},
|
||||
Method,
|
||||
};
|
||||
|
||||
use crate::utils::{net::get_request_user_agent_header, table_builder::TableBuilder};
|
||||
|
||||
|
@ -38,7 +34,7 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Table<
|
|||
Value::String(s) => {
|
||||
let url = s.to_string_lossy().to_string();
|
||||
let method = "GET".to_string();
|
||||
(url, method, None, None)
|
||||
(url, method, HashMap::new(), None)
|
||||
}
|
||||
Value::Table(tab) => {
|
||||
// Extract url
|
||||
|
@ -58,17 +54,14 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Table<
|
|||
// Extract headers
|
||||
let headers = match tab.raw_get::<&str, mlua::Table>("headers") {
|
||||
Ok(config_headers) => {
|
||||
let mut lua_headers = HeaderMap::new();
|
||||
let mut lua_headers = HashMap::new();
|
||||
for pair in config_headers.pairs::<mlua::String, mlua::String>() {
|
||||
let (key, value) = pair?;
|
||||
lua_headers.insert(
|
||||
HeaderName::from_str(key.to_str()?).map_err(Error::external)?,
|
||||
HeaderValue::from_str(value.to_str()?).map_err(Error::external)?,
|
||||
);
|
||||
let (key, value) = pair?.to_owned();
|
||||
lua_headers.insert(key, value);
|
||||
}
|
||||
Some(lua_headers)
|
||||
lua_headers
|
||||
}
|
||||
Err(_) => None,
|
||||
Err(_) => HashMap::new(),
|
||||
};
|
||||
// Extract body
|
||||
let body = match tab.raw_get::<&str, mlua::String>("body") {
|
||||
|
@ -85,58 +78,46 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Table<
|
|||
}
|
||||
};
|
||||
// Convert method string into proper enum
|
||||
let method = match Method::from_str(&method) {
|
||||
Ok(meth) => meth,
|
||||
Err(_) => {
|
||||
let method = method.trim().to_ascii_uppercase();
|
||||
let method = match method.as_ref() {
|
||||
"GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | "PATCH" => &method,
|
||||
_ => {
|
||||
return Err(Error::RuntimeError(format!(
|
||||
"Invalid request config method '{}'",
|
||||
&method
|
||||
)))
|
||||
}
|
||||
};
|
||||
// Extract headers from config, force user agent
|
||||
let mut header_map = if let Some(headers) = headers {
|
||||
headers
|
||||
} else {
|
||||
HeaderMap::new()
|
||||
};
|
||||
header_map.insert(
|
||||
"User-Agent",
|
||||
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
|
||||
let client = reqwest::Client::builder()
|
||||
.build()
|
||||
.map_err(Error::external)?;
|
||||
// Create and send the request
|
||||
let mut request = client.request(method, url).headers(header_map);
|
||||
if let Some(body) = body {
|
||||
request = request.body(body);
|
||||
let mut request = ureq::request(method, &url);
|
||||
for (header, value) in headers {
|
||||
request = request.set(header.to_str()?, value.to_str()?);
|
||||
}
|
||||
let response = request.send().await.map_err(Error::external)?;
|
||||
// Extract status, headers, body
|
||||
let res_status = response.status();
|
||||
let res_headers = response.headers().clone();
|
||||
let res_bytes = response.bytes().await.map_err(Error::external)?;
|
||||
// Construct and return a readonly lua table with results
|
||||
TableBuilder::new(lua)?
|
||||
.with_value("ok", res_status.is_success())?
|
||||
.with_value("statusCode", res_status.as_u16())?
|
||||
.with_value(
|
||||
"statusMessage",
|
||||
res_status.canonical_reason().unwrap_or("?"),
|
||||
)?
|
||||
.with_value(
|
||||
"headers",
|
||||
res_headers
|
||||
let response = request
|
||||
.set("User-Agent", &get_request_user_agent_header()) // Always force user agent
|
||||
.send_bytes(&body.unwrap_or_default());
|
||||
match response {
|
||||
Ok(res) | Err(ureq::Error::Status(_, res)) => {
|
||||
// Extract status, headers
|
||||
let res_status = res.status();
|
||||
let res_status_text = res.status_text().to_owned();
|
||||
let res_header_names = &res.headers_names();
|
||||
let res_headers = res_header_names
|
||||
.iter()
|
||||
.filter_map(|(key, value)| match value.to_str() {
|
||||
Ok(value) => Some((key.as_str(), value)),
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect::<HashMap<_, _>>(),
|
||||
)?
|
||||
.with_value("body", lua.create_string(&res_bytes)?)?
|
||||
.build_readonly()
|
||||
.map(|name| (name.to_owned(), res.header(name).unwrap().to_owned()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
// Read response bytes
|
||||
let mut res_bytes = Vec::new();
|
||||
res.into_reader().read_to_end(&mut res_bytes)?;
|
||||
// Construct and return a readonly lua table with results
|
||||
TableBuilder::new(lua)?
|
||||
.with_value("ok", (200..300).contains(&res_status))?
|
||||
.with_value("statusCode", res_status)?
|
||||
.with_value("statusMessage", res_status_text)?
|
||||
.with_value("headers", res_headers)?
|
||||
.with_value("body", lua.create_string(&res_bytes)?)?
|
||||
.build_readonly()
|
||||
}
|
||||
Err(e) => Err(Error::external(e)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use mlua::{Error, Function, Lua, MetaMethod, Result, Table, Value};
|
||||
use os_str_bytes::RawOsString;
|
||||
use tokio::process::Command;
|
||||
use smol::process::Command;
|
||||
|
||||
use crate::utils::table_builder::TableBuilder;
|
||||
|
||||
|
@ -113,10 +113,7 @@ async fn process_spawn(lua: &Lua, (program, args): (String, Option<Vec<String>>)
|
|||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(mlua::Error::external)?;
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.await
|
||||
.map_err(mlua::Error::external)?;
|
||||
let output = child.output().await.map_err(mlua::Error::external)?;
|
||||
// NOTE: If an exit code was not given by the child process,
|
||||
// we default to 1 if it yielded any error output, otherwise 0
|
||||
let code = output
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, Thread, Value, Variadic};
|
||||
use tokio::time::{self, Instant};
|
||||
use smol::Timer;
|
||||
|
||||
use crate::utils::table_builder::TableBuilder;
|
||||
|
||||
|
@ -78,7 +78,7 @@ async fn task_spawn<'a>(lua: &'a Lua, (tof, args): (Value<'a>, Vararg<'a>)) -> R
|
|||
// the async wait function inside of a coroutine
|
||||
async fn task_wait(_: &Lua, duration: Option<f32>) -> Result<f32> {
|
||||
let start = Instant::now();
|
||||
time::sleep(
|
||||
Timer::after(
|
||||
duration
|
||||
.map(Duration::from_secs_f32)
|
||||
.unwrap_or(Duration::ZERO),
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use mlua::Lua;
|
||||
use tokio::task;
|
||||
use mlua::prelude::*;
|
||||
|
||||
pub mod globals;
|
||||
pub mod utils;
|
||||
|
@ -62,46 +61,33 @@ impl Lune {
|
|||
}
|
||||
|
||||
pub async fn run(&self, name: &str, chunk: &str) -> Result<()> {
|
||||
let run_name = name.to_owned();
|
||||
let run_chunk = chunk.to_owned();
|
||||
let run_globals = self.globals.to_owned();
|
||||
let run_args = self.args.to_owned();
|
||||
// Spawn a thread-local task so that we can then spawn
|
||||
// more tasks in our globals without the Send requirement
|
||||
let local = task::LocalSet::new();
|
||||
local
|
||||
.run_until(async move {
|
||||
task::spawn_local(async move {
|
||||
let lua = Lua::new();
|
||||
for global in &run_globals {
|
||||
match &global {
|
||||
LuneGlobal::Console => create_console(&lua).await?,
|
||||
LuneGlobal::Fs => create_fs(&lua).await?,
|
||||
LuneGlobal::Net => create_net(&lua).await?,
|
||||
LuneGlobal::Process => create_process(&lua, run_args.clone()).await?,
|
||||
LuneGlobal::Task => create_task(&lua).await?,
|
||||
}
|
||||
smol::block_on(async {
|
||||
let lua = Lua::new();
|
||||
for global in &self.globals {
|
||||
match &global {
|
||||
LuneGlobal::Console => create_console(&lua).await?,
|
||||
LuneGlobal::Fs => create_fs(&lua).await?,
|
||||
LuneGlobal::Net => create_net(&lua).await?,
|
||||
LuneGlobal::Process => create_process(&lua, self.args.clone()).await?,
|
||||
LuneGlobal::Task => create_task(&lua).await?,
|
||||
}
|
||||
}
|
||||
let result = lua.load(chunk).set_name(name)?.exec_async().await;
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
if cfg!(test) {
|
||||
bail!(pretty_format_luau_error(&e))
|
||||
} else {
|
||||
bail!(
|
||||
"\n{}\n{}",
|
||||
format_label("ERROR"),
|
||||
pretty_format_luau_error(&e)
|
||||
)
|
||||
}
|
||||
let result = lua.load(&run_chunk).set_name(&run_name)?.exec_async().await;
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
if cfg!(test) {
|
||||
bail!(pretty_format_luau_error(&e))
|
||||
} else {
|
||||
bail!(
|
||||
"\n{}\n{}",
|
||||
format_label("ERROR"),
|
||||
pretty_format_luau_error(&e)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,26 +95,28 @@ impl Lune {
|
|||
mod tests {
|
||||
use crate::Lune;
|
||||
use anyhow::Result;
|
||||
use smol::fs::read_to_string;
|
||||
use std::env::current_dir;
|
||||
use tokio::fs::read_to_string;
|
||||
|
||||
const ARGS: &[&str] = &["Foo", "Bar"];
|
||||
|
||||
macro_rules! run_tests {
|
||||
($($name:ident: $value:expr,)*) => {
|
||||
$(
|
||||
#[tokio::test]
|
||||
async fn $name() -> Result<()> {
|
||||
let path = current_dir()
|
||||
.unwrap()
|
||||
.join(format!("src/tests/{}.luau", $value));
|
||||
let script = read_to_string(&path)
|
||||
.await
|
||||
.unwrap();
|
||||
let lune = Lune::new()
|
||||
.with_args(ARGS.clone().iter().map(ToString::to_string).collect())
|
||||
.with_all_globals();
|
||||
lune.run($value, &script).await
|
||||
#[test]
|
||||
fn $name() -> Result<()> {
|
||||
smol::block_on(async {
|
||||
let path = current_dir()
|
||||
.unwrap()
|
||||
.join(format!("src/tests/{}.luau", $value));
|
||||
let script = read_to_string(&path)
|
||||
.await
|
||||
.unwrap();
|
||||
let lune = Lune::new()
|
||||
.with_args(ARGS.clone().iter().map(ToString::to_string).collect())
|
||||
.with_all_globals();
|
||||
lune.run($value, &script).await
|
||||
})
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue