use std::path::Path;
use mlua::{Lua, MultiValue, Table, Value};
fn record(lua: &Lua, p: &str) {
let path = Path::new(p);
let found = path.is_file().then_some(path);
let _ = crate::with_engine(lua, |e| e.record_probe(p, found));
}
fn wrap_io_open(lua: &Lua) -> mlua::Result<()> {
let rec = lua.create_function(|lua, p: String| {
record(lua, &p);
Ok(())
})?;
lua.load(
r#"
local record = ...
local open = io.open
io.open = function(name, mode)
if type(name) == "string" and (mode == nil or string.sub(mode, 1, 1) == "r") then
record(name)
end
return open(name, mode)
end
"#,
)
.set_name("=[retex io.open]")
.call::<()>(rec)
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
wrap_io_open(lua)?;
let t = lua.create_table()?;
t.set(
"attributes",
lua.create_function(|lua, (p, req): (String, Option<String>)| {
record(lua, &p);
attributes(lua, &p, req, true)
})?,
)?;
t.set(
"symlinkattributes",
lua.create_function(|lua, (p, req): (String, Option<String>)| {
attributes(lua, &p, req, false)
})?,
)?;
t.set(
"isdir",
lua.create_function(|_, p: String| Ok(Path::new(&p).is_dir()))?,
)?;
t.set(
"isfile",
lua.create_function(|lua, p: String| {
record(lua, &p);
Ok(Path::new(&p).is_file())
})?,
)?;
t.set(
"currentdir",
lua.create_function(|_, ()| {
Ok(std::env::current_dir()
.map(|d| d.to_string_lossy().into_owned())
.ok())
})?,
)?;
t.set(
"chdir",
lua.create_function(|lua, p: String| result(lua, std::env::set_current_dir(&p)))?,
)?;
t.set(
"mkdir",
lua.create_function(|lua, p: String| result(lua, std::fs::create_dir(&p)))?,
)?;
t.set(
"mkdirp",
lua.create_function(|lua, p: String| result(lua, std::fs::create_dir_all(&p)))?,
)?;
t.set(
"rmdir",
lua.create_function(|lua, p: String| result(lua, std::fs::remove_dir(&p)))?,
)?;
t.set(
"touch",
lua.create_function(|lua, (p, _a, _m): (String, Option<f64>, Option<f64>)| {
let r = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&p)
.map(|_| ());
result(lua, r)
})?,
)?;
t.set(
"readlink",
lua.create_function(|_, p: String| {
Ok(std::fs::read_link(&p)
.map(|d| d.to_string_lossy().into_owned())
.ok())
})?,
)?;
t.set("shortname", lua.create_function(|_, p: String| Ok(p))?)?;
t.set(
"dir",
lua.create_function(|lua, p: String| {
let mut names: Vec<String> = vec![".".into(), "..".into()];
if let Ok(rd) = std::fs::read_dir(&p) {
for e in rd.flatten() {
names.push(e.file_name().to_string_lossy().into_owned());
}
}
let state = lua.create_table()?;
for (i, n) in names.into_iter().enumerate() {
state.set(i + 1, n)?;
}
state.set("i", 0)?;
let iter = lua.create_function(|_, st: Table| {
let i: i64 = st.get("i")?;
st.set("i", i + 1)?;
st.get::<Value>(i + 1)
})?;
Ok((iter, state))
})?,
)?;
t.set("lock", lua.create_function(|_, _a: MultiValue| Ok(true))?)?;
t.set("unlock", lua.create_function(|_, _a: MultiValue| Ok(true))?)?;
t.set(
"setmode",
lua.create_function(|_, _a: MultiValue| Ok(true))?,
)?;
lua.globals().set("lfs", t.clone())?;
let package: Table = lua.globals().get("package")?;
let loaded: Table = package.get("loaded")?;
loaded.set("lfs", t)?;
Ok(())
}
fn result(lua: &Lua, r: std::io::Result<()>) -> mlua::Result<MultiValue> {
match r {
Ok(()) => Ok(MultiValue::from_iter([Value::Boolean(true)])),
Err(e) => Ok(MultiValue::from_iter([
Value::Nil,
Value::String(lua.create_string(e.to_string())?),
])),
}
}
fn attributes(lua: &Lua, p: &str, req: Option<String>, follow: bool) -> mlua::Result<MultiValue> {
use std::os::unix::fs::{MetadataExt, PermissionsExt};
let md = if follow {
std::fs::metadata(p)
} else {
std::fs::symlink_metadata(p)
};
let md = match md {
Ok(m) => m,
Err(e) => {
return Ok(MultiValue::from_iter([
Value::Nil,
Value::String(lua.create_string(format!("{p}: {e}"))?),
]));
}
};
let ft = md.file_type();
let mode = if ft.is_dir() {
"directory"
} else if ft.is_symlink() {
"link"
} else if ft.is_file() {
"file"
} else {
"other"
};
let perm = md.permissions().mode();
let mut ps = String::new();
for (bit, c) in [
(0o400, 'r'),
(0o200, 'w'),
(0o100, 'x'),
(0o040, 'r'),
(0o020, 'w'),
(0o010, 'x'),
(0o004, 'r'),
(0o002, 'w'),
(0o001, 'x'),
] {
ps.push(if perm & bit != 0 { c } else { '-' });
}
let t = lua.create_table()?;
t.set("mode", mode)?;
t.set("size", md.size())?;
t.set("modification", md.mtime())?;
t.set("access", md.atime())?;
t.set("change", md.ctime())?;
t.set("dev", md.dev())?;
t.set("ino", md.ino())?;
t.set("nlink", md.nlink())?;
t.set("uid", md.uid())?;
t.set("gid", md.gid())?;
t.set("rdev", md.rdev())?;
t.set("blocks", md.blocks())?;
t.set("blksize", md.blksize())?;
t.set("permissions", ps)?;
match req {
Some(r) => Ok(MultiValue::from_iter([t.get::<Value>(r)?])),
None => Ok(MultiValue::from_iter([Value::Table(t)])),
}
}