use mlua::{Lua, MultiValue, Table, Value};
use retex_kpath::FileFormat;
use crate::with_engine;
pub fn format_by_name(s: &str) -> Option<FileFormat> {
Some(match s {
"tex" | "other text files" => FileFormat::Tex,
"lua" => FileFormat::Lua,
"opentype fonts" => FileFormat::OpenType,
"truetype fonts" => FileFormat::TrueType,
"type1 fonts" => FileFormat::Type1,
"tfm" => FileFormat::Tfm,
"map" => FileFormat::Map,
"enc files" | "enc" => FileFormat::Enc,
"vf" => FileFormat::Vf,
"fmt" => FileFormat::Fmt,
"bib" => FileFormat::Bib,
"bst" => FileFormat::Bst,
"graphic/figure" => FileFormat::Pict,
_ => return None,
})
}
fn find(lua: &Lua, name: &str, fmt: FileFormat) -> mlua::Result<Option<String>> {
with_engine(lua, |e| {
let in_output = e
.files
.output_dir
.as_ref()
.filter(|_| std::path::Path::new(name).is_relative())
.map(|d| d.join(name))
.filter(|p| p.is_file());
let found = in_output.or_else(|| {
e.kpse
.as_ref()
.and_then(|k| k.borrow_mut().find_file(name, fmt))
});
e.record_probe(name, found.as_deref());
found.map(|p| p.to_string_lossy().into_owned())
})
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
let t = lua.create_table()?;
t.set(
"find_file",
lua.create_function(|lua, args: MultiValue| {
let mut it = args.into_iter();
let name = match it.next() {
Some(Value::String(s)) => s.to_str()?.to_owned(),
_ => return Ok(Value::Nil),
};
let fmt = match it.next() {
Some(Value::String(s)) => format_by_name(&s.to_str()?).unwrap_or(FileFormat::Tex),
_ => FileFormat::Tex,
};
match find(lua, &name, fmt)? {
Some(p) => Ok(Value::String(lua.create_string(&p)?)),
None => {
with_engine(lua, |e| e.record_absent_file(&name))?;
Ok(Value::Nil)
}
}
})?,
)?;
t.set(
"var_value",
lua.create_function(|lua, name: String| {
with_engine(lua, |e| {
e.kpse.as_ref().and_then(|k| k.borrow().var_value(&name))
})
})?,
)?;
t.set(
"expand_var",
lua.create_function(|lua, s: String| {
let name = s.trim_start_matches('$').to_owned();
with_engine(lua, |e| {
e.kpse
.as_ref()
.and_then(|k| k.borrow().var_value(&name))
.unwrap_or_default()
})
})?,
)?;
t.set(
"show_path",
lua.create_function(|lua, fmt: String| {
let f = format_by_name(&fmt).unwrap_or(FileFormat::Tex);
with_engine(lua, |e| {
e.kpse
.as_ref()
.map(|k| k.borrow_mut().search_path(f).join(":"))
.unwrap_or_default()
})
})?,
)?;
t.set(
"set_program_name",
lua.create_function(|_, _a: MultiValue| Ok(()))?,
)?;
t.set(
"version",
lua.create_function(|_, ()| Ok("kpathsea version 6.4.1"))?,
)?;
t.set(
"new",
lua.create_function(|lua, _a: MultiValue| lua.globals().get::<Table>("kpse"))?,
)?;
lua.globals().set("kpse", t)?;
let package: Table = lua.globals().get("package")?;
let searchers: Table = package.get("searchers")?;
let searcher = lua.create_function(|lua, name: String| {
let file = name.replace('.', "/");
let found = find(lua, &file, FileFormat::Lua)?.or(find(lua, &name, FileFormat::Lua)?);
match found {
Some(path) => {
let mut code = std::fs::read(&path).map_err(mlua::Error::external)?;
if code.first() == Some(&b'#') {
let end = code.iter().position(|&b| b == b'\n').unwrap_or(code.len());
code.drain(..end);
}
let chunk_name = format!("@{path}");
let f = lua.load(code).set_name(chunk_name).into_function()?;
Ok(MultiValue::from_iter([
Value::Function(f),
Value::String(lua.create_string(&path)?),
]))
}
None => Ok(MultiValue::from_iter([Value::String(lua.create_string(
format!("\n\t[kpse lua searcher] file not found: '{name}'"),
)?)])),
}
})?;
let n = searchers.raw_len();
for i in (2..=n).rev() {
let v: Value = searchers.raw_get(i)?;
searchers.raw_set(i + 1, v)?;
}
searchers.raw_set(2, searcher)?;
Ok(())
}