From db4faea821aaceda7ed4a6bb92c54a6eaf0573e9 Mon Sep 17 00:00:00 2001 From: luaneko Date: Fri, 27 Jun 2025 01:36:08 +1000 Subject: [PATCH] Implement walk_dir and more robust globbing --- Cargo.lock | 44 ++++++++++++++++- crates/lb/Cargo.toml | 6 +-- crates/lb/src/fs.rs | 115 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 146 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 148146b..0192bf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -691,6 +691,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + [[package]] name = "h2" version = "0.4.10" @@ -1032,12 +1045,13 @@ name = "lb" version = "0.0.1" dependencies = [ "derive_more", - "glob", + "globset", "luaffi", "luajit", "sysexits", "thiserror", "tokio", + "walkdir", ] [[package]] @@ -1579,6 +1593,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "semver" version = "1.0.26" @@ -2070,6 +2093,16 @@ dependencies = [ "rustversion", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2121,6 +2154,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/crates/lb/Cargo.toml b/crates/lb/Cargo.toml index 0005354..01eac32 100644 --- a/crates/lb/Cargo.toml +++ b/crates/lb/Cargo.toml @@ -9,15 +9,15 @@ repository.workspace = true [features] task = ["tokio/rt", "tokio/time"] -fs = ["tokio/fs", "dep:glob"] +fs = ["tokio/fs", "dep:walkdir", "dep:globset"] net = ["tokio/net"] -glob = ["dep:glob"] [dependencies] derive_more = { version = "2.0.1", features = ["full"] } -glob = { version = "0.3.2", optional = true } +globset = { version = "0.4.16", optional = true } luaffi = { path = "../luaffi" } luajit = { path = "../luajit" } sysexits = "0.9.0" thiserror = "2.0.12" tokio = { version = "1.45.1" } +walkdir = { version = "2.5.0", optional = true } diff --git a/crates/lb/src/fs.rs b/crates/lb/src/fs.rs index 31fdf8d..bc825dc 100644 --- a/crates/lb/src/fs.rs +++ b/crates/lb/src/fs.rs @@ -14,7 +14,7 @@ //! See [`lb_fslib`] for items exported by this library. use derive_more::From; use luaffi::{cdef, metatype}; -use std::time::SystemTime; +use std::{path::PathBuf, time::SystemTime}; use thiserror::Error; /// Errors that can be thrown by this library. @@ -26,12 +26,12 @@ pub enum Error { /// I/O error. #[error("{0}")] Io(#[from] std::io::Error), - /// Glob error. + /// Walk directory error. #[error("{0}")] - Glob(#[from] glob::GlobError), + Walk(#[from] walkdir::Error), /// Glob pattern error. #[error("{0}")] - GlobPattern(#[from] glob::PatternError), + Glob(#[from] globset::Error), } type Result = std::result::Result; @@ -77,8 +77,26 @@ impl lb_fslib { Ok(std::fs::read_dir(path)?.into()) } - pub extern "Lua-C" fn glob(&self, pattern: &str) -> Result { - Ok(glob::glob(pattern)?.into()) + pub extern "Lua-C" fn walk_dir(&self, path: &str) -> lb_walk_dir { + walkdir::WalkDir::new(path).into_iter().into() + } + + pub extern "Lua-C" fn glob(&self, pattern: &str) -> Result { + self.glob_dir(".", pattern) + } + + pub extern "Lua-C" fn glob_dir(&self, path: &str, pattern: &str) -> Result { + let prefix = PathBuf::from(path); + let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter(); + let matcher = globset::GlobSet::builder() + .add(globset::Glob::new(pattern)?) + .build()?; + + Ok(lb_glob_dir { + iter, + matcher, + prefix, + }) } } @@ -300,19 +318,86 @@ impl lb_file_perms { } } -/// Iterator that yields paths from the filesystem that match a particular pattern. +/// Iterator for recursively descending into a directory. #[derive(Debug, From)] #[cdef] -pub struct lb_glob_paths(#[opaque] glob::Paths); +pub struct lb_walk_dir(#[opaque] walkdir::IntoIter); #[metatype] -impl lb_glob_paths { +impl lb_walk_dir { #[call] - pub extern "Lua-C" fn next(&mut self) -> Result> { - Ok(self - .0 - .next() - .transpose()? - .map(|s| s.to_string_lossy().into())) + pub extern "Lua-C" fn next(&mut self) -> Result> { + Ok(self.0.next().transpose()?.map(Into::into)) + } +} + +/// Entry inside of a directory on the filesystem obtained from [`lb_walk_dir`]. +#[derive(Debug, From)] +#[cdef] +pub struct lb_walk_dir_entry(#[opaque] walkdir::DirEntry); + +#[metatype] +impl lb_walk_dir_entry { + pub extern "Lua-C" fn path(&self) -> String { + self.0.path().to_string_lossy().into() + } + + pub extern "Lua-C" fn name(&self) -> String { + self.0.file_name().to_string_lossy().into() + } + + pub extern "Lua-C" fn r#type(&self) -> lb_file_type { + self.0.file_type().into() + } + + pub extern "Lua-C" fn metadata(&self) -> Result { + Ok(self.0.metadata()?.into()) + } + + pub extern "Lua-C" fn is_symlink(&self) -> bool { + self.0.path_is_symlink() + } + + pub extern "Lua-C" fn depth(&self) -> u32 { + self.0.depth() as u32 + } + + #[cfg(unix)] + pub extern "Lua-C" fn ino(&self) -> u64 { + use walkdir::DirEntryExt; + self.0.ino() + } + + #[tostring] + pub extern "Lua" fn tostring(&self) -> String { + self.path() + } +} + +/// Iterator that yields paths from the filesystem that match a particular pattern. +#[derive(Debug)] +#[cdef] +pub struct lb_glob_dir { + #[opaque] + iter: walkdir::IntoIter, + #[opaque] + matcher: globset::GlobSet, + #[opaque] + prefix: PathBuf, +} + +#[metatype] +impl lb_glob_dir { + #[call] + pub extern "Lua-C" fn next(&mut self) -> Result> { + while let Some(res) = self.iter.next() { + let entry = res?; + let path = entry.path().strip_prefix(&self.prefix).unwrap(); + if self.matcher.is_match(path) { + return Ok(Some(entry.into())); + } + } + + Ok(None) } }