pub mod error;
pub mod path_obj;
use crate::userscript_api::{
fs_api::{error::Error, path_obj::PathObj},
include::{LuaEither, LuaExternalError, LuaUserData, LuaUserDataMethods, LuaUserDataRef},
ApiObject,
};
use std::path::PathBuf;
pub struct FsApi;
impl LuaUserData for FsApi {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method(
"path",
|_, _, path: PathBuf| async move { Ok(PathObj(path)) },
);
methods.add_async_method(
"test",
|_, _, path: LuaEither<PathBuf, LuaUserDataRef<PathObj>>| async move {
let path: PathBuf = match path {
LuaEither::Left(pb) => pb,
LuaEither::Right(po) => po.0.clone(),
};
let Ok(path) = path.canonicalize() else {
return Ok(false);
};
if path.is_symlink() {
return Ok(path.read_link().is_ok());
}
if path.is_dir() {
return Ok(path.read_dir().is_ok());
}
if path.is_file() {
return Ok(std::fs::File::open(path).is_ok());
}
Ok(true)
},
);
methods.add_async_method(
"listdir",
|_, _, path: LuaEither<PathBuf, LuaUserDataRef<PathObj>>| async move {
let path: PathBuf = match path {
LuaEither::Left(pb) => pb,
LuaEither::Right(po) => po.0.clone(),
};
if !path.is_dir() {
return Err(Error::NotADirectory { path }.into_lua_err());
}
let mut subpaths: Vec<PathObj> = Vec::with_capacity(16384);
let path: PathBuf = path
.canonicalize()
.map_err(|source| error::Error::InvalidPath { path, source })?;
for entry in path
.read_dir()
.map_err(|source| error::Error::ReadDirError {
path: path.clone(),
source,
})?
{
let Ok(entry) = entry else { continue };
subpaths.push(PathObj(entry.path()));
}
subpaths.shrink_to_fit();
Ok(subpaths)
},
);
methods.add_async_method(
"walk",
|_, _, basepath: LuaEither<PathBuf, LuaUserDataRef<PathObj>>| async move {
let basepath: PathBuf = match basepath {
LuaEither::Left(pb) => pb,
LuaEither::Right(po) => po.0.clone(),
};
if !basepath.is_dir() {
return Err(Error::NotADirectory { path: basepath }.into_lua_err());
}
let mut dirq: Vec<PathBuf> = Vec::with_capacity(16384);
let mut path_objs: Vec<PathObj> = Vec::with_capacity(16384);
dirq.push(basepath.canonicalize()?);
while let Some(current_dir) = dirq.pop() {
path_objs.push(PathObj(current_dir.clone()));
if let Ok(dir_reader) = current_dir.read_dir() {
for entry in dir_reader {
let Ok(entry) = entry else {
continue;
};
let path: PathBuf = entry.path();
if !path.is_symlink() && path.is_dir() {
dirq.push(path.clone());
}
path_objs.push(PathObj(path));
}
}
}
path_objs.sort();
path_objs.dedup_by(|a: &mut PathObj, b: &mut PathObj| a.0.eq(&b.0));
path_objs.shrink_to_fit();
Ok(path_objs)
},
);
}
}
impl ApiObject for FsApi {
fn name(&self) -> &'static str {
"fs"
}
}