use anyhow::Result;
use ghee_lang::Key;
use ghee_lang::Xattr;
use thiserror::Error;
use std::fs::hard_link;
use std::fs::create_dir_all;
use std::path::Path;
use walkdir::WalkDir;
use crate::declare_closure_indices;
use crate::paths::sub_idx_path;
use std::path::PathBuf;
use super::init;
#[derive(Error, Debug)]
pub enum IdxErr {
#[error("IO error when getting xattr")]
GetIoError(std::io::Error),
#[error("Got empty value for xattr {0}")]
GotEmptyValue(Xattr),
#[error("Error creating directory {path}: {err}")]
CreateDirIoError { path: PathBuf, err: std::io::Error },
#[error("Error hard linking {original} to {link}")]
HardLinkError {
original: PathBuf,
link: PathBuf,
err: std::io::Error,
},
}
pub fn idx(src: &PathBuf, dest: Option<&PathBuf>, keys: &Key, verbose: bool) -> Result<()> {
if keys.is_empty() {
panic!("No keys were provided to index by");
}
let dest = dest.cloned().unwrap_or_else(|| {
sub_idx_path(src, keys).unwrap_or_else(|e| panic!("Could not get sub-index path: {}", e))
});
for entry in WalkDir::new(&src) {
let entry = entry.unwrap_or_else(|e| panic!("Could not read directory: {}", e));
if entry.file_type().is_dir() {
continue;
}
let mut key = PathBuf::new();
for maybe_subkey in keys.iter().map(|subkey| {
let subkey_osstring = subkey.to_osstring();
(xattr::get(entry.path(), subkey_osstring), subkey)
}) {
let subkey = maybe_subkey
.0
.map_err(IdxErr::GetIoError)?
.ok_or(IdxErr::GotEmptyValue(maybe_subkey.1.clone()))?;
let slice = &subkey[..];
let str = String::from_utf8_lossy(slice);
let str_ref = str.as_ref();
let path = Path::new(str_ref);
key.push(path);
}
let path = dest.join(key.as_path());
let dir = path.parent().unwrap();
create_dir_all(dir).map_err(|e| IdxErr::CreateDirIoError {
path: dir.to_path_buf(),
err: e,
})?;
hard_link(entry.path(), &path).map_err(|err| IdxErr::HardLinkError {
original: entry.path().to_path_buf(),
link: path.clone(),
err,
})?;
if verbose {
eprintln!("{} -> {}", entry.path().display(), path.display());
}
}
init(&dest, &keys, false)?;
declare_closure_indices(src, &dest)
}
#[cfg(test)]
mod test {
use ghee_lang::Key;
use crate::{cmd::init, table_info, test_support::TempDirAuto};
use super::idx;
#[test]
fn test_idx() {
let dir1 = TempDirAuto::new("ghee-test-idx:1");
let dir2 = TempDirAuto::new("ghee-test-idx:2");
let dir3 = TempDirAuto::new("ghee-test-idx:3");
let key1 = Key::from(vec!["test1"]);
let key2 = Key::from(vec!["test2"]);
let key3 = Key::from_string("test3");
init(&dir1, &key1, false).unwrap();
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert!(info1.indices_abs().contains_key(&key1));
assert_eq!(info1.index_path_abs(&key1), &dir1.dir);
assert_eq!(info1.indices_abs().len(), 1);
}
idx(&dir1, Some(&dir2), &key2, false).unwrap();
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert!(info1.indices_abs().contains_key(&key2));
assert_eq!(info1.index_path_abs(&key2), &dir2.dir);
assert_eq!(info1.indices_abs().len(), 2);
assert_eq!(info1.key(), &key1);
}
{
let info2 = table_info(&dir2).unwrap().unwrap();
assert!(info2.indices_abs().contains_key(&key1));
assert_eq!(info2.index_path_abs(&key1), &dir1.dir);
assert_eq!(info2.indices_abs().len(), 2);
assert_eq!(info2.key(), &key2);
}
idx(&dir1, Some(&dir3), &key3, false).unwrap();
let info = table_info(&dir1).unwrap().unwrap();
let indices = info.indices_abs();
assert_eq!(indices.len(), 3);
assert!(indices.contains_key(&key1));
assert!(indices.contains_key(&key2));
assert!(indices.contains_key(&key3));
}
#[test]
fn test_idx_chain() {
let dir1 = TempDirAuto::new("ghee-test-idx-chain:1");
let dir2 = TempDirAuto::new("ghee-test-idx-chain:2");
let dir3 = TempDirAuto::new("ghee-test-idx-chain:3");
let key1 = Key::from_string("test1");
let key2 = Key::from_string("test2");
let key3 = Key::from_string("test3");
init(&dir1, &key1, false).unwrap();
idx(&dir1, Some(&dir2), &key2, false).unwrap();
idx(&dir2, Some(&dir3), &key3, false).unwrap();
for dir in vec![&dir1, &dir2, &dir3] {
let info = table_info(dir).unwrap().unwrap();
for key in vec![&key1, &key2, &key3] {
assert!(
info.indices_abs().contains_key(key),
"Table at {} does not contain key {:?}",
dir.display(),
key
);
}
}
}
}