From 091f0c8eb5ed95462712d1fae2ce6903c33d097a Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 2 Jul 2025 00:08:58 +0800 Subject: [PATCH] feat: impl lua engine --- src/http/lua.rs | 21 ++++++++++++-- src/http/mod.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/http/lua.rs b/src/http/lua.rs index 9861ffb..a96b97a 100644 --- a/src/http/lua.rs +++ b/src/http/lua.rs @@ -6,15 +6,22 @@ use axum::{ }; use axum_extra::extract::Host; use http::Uri; +use mlua::{UserData, Value}; +use tokio::fs::{self}; use tracing::error; use crate::{ - http::{HOSTS, error::RouteError, serve::resolve_parent_path}, + http::{HOSTS, LUA_ENGINE, error::RouteError, serve::resolve_parent_path}, utils::parse_port_from_host, }; use super::error::RouteResult; +#[derive(Clone, Debug)] +struct Candy {} + +impl UserData for Candy {} + pub async fn lua( req_uri: Uri, path: Option>, @@ -44,6 +51,16 @@ pub async fn lua( .as_ref() .ok_or(RouteError::InternalError()) .with_context(|| "lua script not found")?; - error!("Lua script: {lua_script}"); + + let lua = &LUA_ENGINE.lua; + let script = fs::read_to_string(lua_script) + .await + .with_context(|| format!("Failed to read lua script file: {lua_script}",))?; + let data: Value = lua.load(script).eval_async().await.map_err(|err| { + error!("Lua script {lua_script} exec error: {err}"); + RouteError::InternalError() + })?; + tracing::debug!("Lua script: {data:?}"); + Ok(()) } diff --git a/src/http/mod.rs b/src/http/mod.rs index 3cc1dc7..bf0fae8 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,10 +1,14 @@ -use std::{net::SocketAddr, sync::LazyLock, time::Duration}; +use std::{ + net::SocketAddr, + sync::{Arc, LazyLock}, + time::Duration, +}; -use anyhow::anyhow; +use anyhow::{Context, anyhow}; use axum::{Router, extract::DefaultBodyLimit, middleware, routing::get}; use axum_server::{Handle, tls_rustls::RustlsConfig}; use dashmap::DashMap; -use mlua::Lua; +use mlua::{Lua, Value}; use tower::ServiceBuilder; use tower_http::{compression::CompressionLayer, timeout::TimeoutLayer}; use tracing::{debug, info, warn}; @@ -36,8 +40,70 @@ pub mod lua; /// } pub static HOSTS: LazyLock> = LazyLock::new(DashMap::new); +pub struct LuaEngine { + pub lua: Lua, + pub shared_table: Arc>, +} +impl LuaEngine { + pub fn new() -> Self { + let lua = Lua::new(); + let shared_table: DashMap = DashMap::new(); + let shared_table = Arc::new(shared_table); + + let module = lua.create_table().expect("create table failed"); + let shared_api = lua.create_table().expect("create shared table failed"); + + // 创建共享字典到 lua 中 + let shared_table_get = shared_table.clone(); + shared_api + .set( + "set", + lua.create_function(move |_, (key, value): (String, String)| { + let t = shared_table_get + .insert(key, value.clone()) + .ok_or(anyhow!("key not found"))?; + Ok(t.clone()) + }) + .expect("create set function failed"), + ) + .expect("set failed"); + let shared_table_get = shared_table.clone(); + shared_api + .set( + "get", + lua.create_function(move |_, key: String| { + let t = shared_table_get.get(&key).ok_or(anyhow!("key not found"))?; + Ok(t.clone()) + }) + .expect("create get function failed"), + ) + .expect("get failed"); + module + .set("shared", shared_api) + .expect("set shared_api failed"); + + // 日志函数 + module + .set( + "log", + lua.create_function(move |_, msg: String| { + tracing::info!("Lua: {}", msg); + Ok(()) + }) + .expect("create log function failed"), + ) + .expect("set log failed"); + + // 全局变量 candy + lua.globals() + .set("candy", module) + .expect("set candy failed"); + + Self { lua, shared_table } + } +} /// lua 脚本执行器 -pub static LUA_EXECUTOR: LazyLock = LazyLock::new(Lua::new); +pub static LUA_ENGINE: LazyLock = LazyLock::new(LuaEngine::new); pub async fn make_server(host: SettingHost) -> anyhow::Result<()> { let mut router = Router::new();