diff options
| -rw-r--r-- | Cargo.lock | 72 | ||||
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/luax/mod.rs | 67 | ||||
| -rw-r--r-- | src/main.rs | 166 | ||||
| -rw-r--r-- | src/server.rs | 11 |
5 files changed, 236 insertions, 83 deletions
@@ -93,6 +93,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] name = "bstr" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -202,6 +208,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] name = "eyre" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -212,6 +228,12 @@ dependencies = [ ] [[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -417,6 +439,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -519,6 +547,15 @@ dependencies = [ ] [[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] name = "reluax" version = "0.1.0" dependencies = [ @@ -528,6 +565,7 @@ dependencies = [ "hyper", "hyper-util", "rlua", + "tempfile", "tokio", "utf8-read", ] @@ -538,18 +576,18 @@ version = "0.19.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d33e5ba15c3d43178f283ed5863d4531e292fc0e56fb773f3bea45f18e3a42a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bstr", "libc", "num-traits", - "rlua-lua54-sys", + "rlua-lua51-sys", ] [[package]] -name = "rlua-lua54-sys" +name = "rlua-lua51-sys" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aafabafe1895cb4a2be81a56d7ff3d46bf4b5d2f9cfdbea2ed404cdabe96474" +checksum = "5c8f4636b03fa54b5c154415f1d9333cec7a7e3bd6bc64c092a2196e488cfd52" dependencies = [ "cc", "libc", @@ -563,6 +601,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -608,6 +659,19 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] name = "thread_local" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -11,6 +11,7 @@ color-eyre = "0.6.2" http-body-util = "0.1.0" hyper = { version = "1.1.0", features = ["http1", "http2", "server"] } hyper-util = { version = "0.1.2", features = ["http1", "http2", "server", "tokio"] } -rlua = "0.19.7" +rlua = { version = "0.19.7", default-features = false, features = ["system-luajit"] } +tempfile = "3.9.0" tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } utf8-read = "0.4.0" diff --git a/src/luax/mod.rs b/src/luax/mod.rs index df9f57d..2e6fe27 100644 --- a/src/luax/mod.rs +++ b/src/luax/mod.rs @@ -1,4 +1,4 @@ -use crate::Result; +use crate::{error::LuaXError, Result}; mod lexer; mod preprocessor; @@ -6,6 +6,63 @@ mod preprocessor; mod tests; mod tokens; +pub fn table_to_html<W: std::io::Write>(table: rlua::Table, f: &mut W) -> Result<()> { + let type_name: Option<String> = table.get("tag").unwrap(); + + if type_name.is_none() { + return Err(LuaXError::MissingField("tag".to_string()).into()); + } + + let type_name = type_name.unwrap(); + + write!(f, "<{}", type_name)?; + let mut children = None; + let mut attrs = None; + for pair in table.pairs::<String, rlua::Value>() { + let (key, value) = pair?; + if key == "tag" { + continue; + } + if key == "children" { + children = Some(value); + continue; + } + if key == "attrs" { + attrs = Some(value); + continue; + } + } + if let Some(attrs) = attrs { + if let rlua::Value::Table(attrs) = attrs { + for pair in attrs.pairs::<String, rlua::String>() { + let (key, value) = pair?; + write!(f, " {}=\"{}\"", key, value.to_str()?)?; + } + } else { + return Err(LuaXError::NonTableAttrs.into()); + } + } + write!(f, ">")?; + + if let Some(children) = children { + if let rlua::Value::Table(children) = children { + for child in children.sequence_values::<rlua::Value>() { + match child? { + rlua::Value::Table(child) => table_to_html(child, f)?, + rlua::Value::String(s) => write!(f, "{}", s.to_str()?)?, + _ => return Err(LuaXError::NonTableChildren.into()), + } + } + } else { + return Err(LuaXError::NonTableChildren.into()); + } + } + + write!(f, "</{}>", type_name)?; + + Ok(()) +} + pub fn preprocess(s: &str) -> Result<String> { let mut buf = Vec::new(); let preprocessor = preprocessor::Preprocessor::new(s, &mut buf)?; @@ -17,12 +74,13 @@ pub fn preprocess(s: &str) -> Result<String> { Ok(s) } -pub fn preprocess_dir(path: &std::path::Path) -> Result<()> { +pub fn preprocess_dir(path: &std::path::Path) -> Result<usize> { + let mut preprocessed = 0; for entry in std::fs::read_dir(path)? { let entry = entry?; let path = entry.path(); if path.is_dir() { - preprocess_dir(&path)?; + preprocessed += preprocess_dir(&path)?; } else { if path.extension().unwrap_or_default() != "luax" { continue; @@ -33,8 +91,9 @@ pub fn preprocess_dir(path: &std::path::Path) -> Result<()> { let out_path = path.with_extension("lua"); std::fs::write(out_path, s)?; + preprocessed += 1; } } - Ok(()) + Ok(preprocessed) } diff --git a/src/main.rs b/src/main.rs index 466ffbc..f4d06ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,76 +1,20 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use clap::Parser; -use color_eyre::Result; -use error::{LuaXError, ReluaxError}; +use color_eyre::{owo_colors::OwoColorize, Result}; mod error; mod luax; mod server; -fn table_to_html<W: std::io::Write>(table: rlua::Table, f: &mut W) -> Result<()> { - let type_name: Option<String> = table.get("tag").unwrap(); - - if type_name.is_none() { - return Err(ReluaxError::LuaX(LuaXError::MissingField("tag".to_string())).into()); - } - - let type_name = type_name.unwrap(); - - write!(f, "<{}", type_name)?; - let mut children = None; - let mut attrs = None; - for pair in table.pairs::<String, rlua::Value>() { - let (key, value) = pair?; - if key == "tag" { - continue; - } - if key == "children" { - children = Some(value); - continue; - } - if key == "attrs" { - attrs = Some(value); - continue; - } - } - if let Some(attrs) = attrs { - if let rlua::Value::Table(attrs) = attrs { - for pair in attrs.pairs::<String, rlua::String>() { - let (key, value) = pair?; - write!(f, " {}=\"{}\"", key, value.to_str()?)?; - } - } else { - return Err(ReluaxError::LuaX(LuaXError::NonTableAttrs).into()); - } - } - write!(f, ">")?; - - if let Some(children) = children { - if let rlua::Value::Table(children) = children { - for child in children.sequence_values::<rlua::Value>() { - match child? { - rlua::Value::Table(child) => table_to_html(child, f)?, - rlua::Value::String(s) => write!(f, "{}", s.to_str()?)?, - _ => return Err(ReluaxError::LuaX(LuaXError::NonTableChildren).into()), - } - } - } else { - return Err(ReluaxError::LuaX(LuaXError::NonTableChildren).into()); - } - } - - write!(f, "</{}>", type_name)?; - - Ok(()) -} - #[derive(Debug, Clone, clap::Parser)] struct Args { #[clap(short = 'C', long = "current-dir", default_value = ".")] current_dir: std::path::PathBuf, #[clap(short = 'p', long = "port", default_value = "4310")] port: u16, + #[clap(short = 'l', long = "local", default_value = "false")] + local: bool, } #[tokio::main] @@ -78,22 +22,86 @@ async fn main() -> color_eyre::Result<()> { color_eyre::install().unwrap(); let args = Args::parse(); - let current_dir = args.current_dir; + if !args.current_dir.is_dir() { + return Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("{} is not a directory", args.current_dir.display()), + ) + .into()); + } + + println!( + "🌴 Project root: {}", + args.current_dir.display().bright_yellow() + ); + + if args.local { + local(args).await + } else { + temp(args).await + } +} + +async fn local(args: Args) -> Result<()> { + println!("🌴 Running in local mode"); + std::env::set_current_dir(&args.current_dir)?; + preprocess_current_dir().await?; + + ensure_entry_point().await?; + + serve(args).await +} + +async fn temp(args: Args) -> Result<()> { + // Create a /tmp/reluax-XXXXXX directory for the server to pre-process files in. + let tmp_dir = tempfile::Builder::new() + .prefix("reluax-") + .tempdir() + .unwrap(); - if !current_dir.is_dir() { + if !tmp_dir.path().is_dir() { return Err(std::io::Error::new( std::io::ErrorKind::NotFound, - format!("{} is not a directory", current_dir.display()), + format!("{} is not a directory", tmp_dir.path().display()), ) .into()); } + println!( + "⏲️ Created temporary directory {}", + tmp_dir.path().display().bright_blue() + ); - std::env::set_current_dir(current_dir)?; + println!( + "⏲️ Will serve from {}", + tmp_dir.path().display().bright_blue() + ); + + // copy all files from the current directory to the temporary directory recursively. + let copied = recurse_copy(&args.current_dir, tmp_dir.path())?; + + println!("⏲️ {} files copied", copied.bright_green()); + + std::env::set_current_dir(tmp_dir.path())?; + + preprocess_current_dir().await?; + + ensure_entry_point().await?; + + serve(args).await +} - luax::preprocess_dir(std::env::current_dir()?.as_path())?; +async fn preprocess_current_dir() -> Result<()> { + let preprocessed = luax::preprocess_dir(std::env::current_dir()?.as_path())?; - println!("⛱️ Reluax files preprocessed!"); + println!( + "⛱️ {} Reluax files preprocessed!", + preprocessed.bright_green() + ); + Ok(()) +} + +async fn ensure_entry_point() -> Result<()> { let entry = PathBuf::from("reluax.lua"); if !entry.is_file() { @@ -104,7 +112,29 @@ async fn main() -> color_eyre::Result<()> { .into()); } - server::Server::serve(args.port).await?; - Ok(()) } + +async fn serve(args: Args) -> Result<()> { + server::Server::serve(args.port).await +} + +fn recurse_copy(from: &Path, to: &Path) -> Result<usize> { + let mut copied = 0; + for entry in std::fs::read_dir(from)? { + let entry = entry?; + let path = entry.path(); + let file_name = path.file_name().unwrap(); + let to = to.join(file_name); + + if path.is_dir() { + std::fs::create_dir(&to)?; + copied += recurse_copy(&path, &to)?; + } else { + std::fs::copy(&path, &to)?; + copied += 1; + } + } + + Ok(copied) +} diff --git a/src/server.rs b/src/server.rs index 4e3812f..106f9f8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,10 +12,12 @@ use hyper_util::rt::TokioIo; use tokio::net::TcpListener; use crate::error::ReluaxError; -use crate::{table_to_html, Result}; +use crate::luax::table_to_html; +use color_eyre::Result; use rlua::Lua; pub struct Server { + port: u16, lua: Arc<Mutex<Lua>>, } @@ -23,20 +25,17 @@ struct State { lua: Arc<Mutex<Lua>>, } -static PORT: std::sync::OnceLock<u16> = std::sync::OnceLock::new(); - impl Server { pub async fn serve(port: u16) -> Result<()> { - PORT.set(port).unwrap(); - let server = Self { + port, lua: Arc::new(Mutex::new(Lua::new())), }; server.start().await } async fn start(self) -> Result<()> { - let addr = SocketAddr::from(([127, 0, 0, 1], *PORT.get().unwrap())); + let addr = SocketAddr::from(([127, 0, 0, 1], self.port)); let listener = TcpListener::bind(addr).await?; loop { |
