98 lines
3.0 KiB
Rust
98 lines
3.0 KiB
Rust
use miette::{Context, IntoDiagnostic};
|
|
use mlua::prelude::*;
|
|
use sled::IVec;
|
|
use std::{collections::HashSet, fs::File, io, io::Read, path::Path, rc::Rc};
|
|
use walkdir::WalkDir;
|
|
use xxhash_rust::xxh3::Xxh3;
|
|
|
|
use crate::{PathExt, PathStatus};
|
|
|
|
pub fn get_obj(lua: &Lua, db: Rc<sled::Db>) -> miette::Result<LuaTable> {
|
|
let tree = Rc::new(
|
|
db.open_tree(b"hashes")
|
|
.into_diagnostic()
|
|
.wrap_err("Failed to open hashes subtree")?,
|
|
);
|
|
|
|
let table = lua
|
|
.create_table()
|
|
.into_diagnostic()
|
|
.wrap_err("Failed to create util table")?;
|
|
|
|
table
|
|
.set(
|
|
"has_changed",
|
|
lua.create_function(move |_lua, (path, ignored): (String, _)| {
|
|
l_has_changed(&path, Rc::clone(&tree), ignored)
|
|
})
|
|
.into_diagnostic()
|
|
.wrap_err("Failed to create has_changed lua function")?,
|
|
)
|
|
.into_diagnostic()
|
|
.wrap_err("Failed to set has_changed")?;
|
|
|
|
Ok(table)
|
|
}
|
|
|
|
fn l_has_changed(path_str: &str, db: Rc<sled::Tree>, ignored: HashSet<String>) -> LuaResult<bool> {
|
|
let path = Path::new(path_str);
|
|
let mut hasher = Xxh3::new();
|
|
let ignored = ignored.iter().map(|s| Path::new(s)).collect::<Vec<_>>();
|
|
|
|
match path.status() {
|
|
PathStatus::NonExistant => Ok(false),
|
|
PathStatus::File => {
|
|
let file = File::open(path)?;
|
|
append_read_hash(&mut hasher, file)?;
|
|
|
|
let hash = IVec::from(&hasher.digest().to_be_bytes());
|
|
let eq = db.get(path_str.as_bytes()).map_err(LuaError::external)? != Some(hash.clone());
|
|
|
|
db.insert(path_str.as_bytes(), hash)
|
|
.map_err(LuaError::external)?;
|
|
|
|
Ok(eq)
|
|
},
|
|
PathStatus::Directory => {
|
|
let walk = WalkDir::new(path);
|
|
for entry in walk {
|
|
let entry = entry.map_err(LuaError::external)?;
|
|
let cur_path = entry.path();
|
|
|
|
if cur_path.is_file() {
|
|
let relpath = cur_path.strip_prefix(path).map_err(LuaError::external)?;
|
|
if ignored.iter().any(|p| relpath.starts_with(p)) {
|
|
continue;
|
|
}
|
|
|
|
let file = File::open(cur_path)?;
|
|
|
|
append_read_hash(&mut hasher, file)?;
|
|
}
|
|
}
|
|
|
|
let hash = IVec::from(&hasher.digest().to_be_bytes());
|
|
let eq = db.get(path_str.as_bytes()).map_err(LuaError::external)? != Some(hash.clone());
|
|
|
|
db.insert(path_str.as_bytes(), hash)
|
|
.map_err(LuaError::external)?;
|
|
|
|
Ok(eq)
|
|
},
|
|
}
|
|
}
|
|
|
|
fn append_read_hash(hasher: &mut Xxh3, mut read: impl Read) -> io::Result<()> {
|
|
let mut buf = [0u8; 1024];
|
|
|
|
loop {
|
|
match read.read(&mut buf) {
|
|
Ok(0) => return Ok(()),
|
|
Ok(l) => hasher.update(&buf[0..l]),
|
|
|
|
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
|
|
Err(e) => return Err(e),
|
|
}
|
|
}
|
|
}
|