Implement walk_dir and more robust globbing

This commit is contained in:
2025-06-27 01:36:08 +10:00
parent 2964ebbe1b
commit db4faea821
3 changed files with 146 additions and 19 deletions

View File

@@ -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 }

View File

@@ -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<T> = std::result::Result<T, Error>;
@@ -77,8 +77,26 @@ impl lb_fslib {
Ok(std::fs::read_dir(path)?.into())
}
pub extern "Lua-C" fn glob(&self, pattern: &str) -> Result<lb_glob_paths> {
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<lb_glob_dir> {
self.glob_dir(".", pattern)
}
pub extern "Lua-C" fn glob_dir(&self, path: &str, pattern: &str) -> Result<lb_glob_dir> {
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<Option<String>> {
Ok(self
.0
.next()
.transpose()?
.map(|s| s.to_string_lossy().into()))
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> {
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<lb_file_meta> {
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<Option<lb_walk_dir_entry>> {
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)
}
}