Standardize on absolute paths in TableInfo, thus fulfilling the ancient prophecy that the example output would be preserved.

This commit is contained in:
Josh Hansen 2023-09-26 22:14:35 -07:00
parent 8b1af42ea3
commit 7a95a15e4f
13 changed files with 79 additions and 158 deletions

View file

@ -23,7 +23,7 @@ Initialized ./people/Darrel
Linked ./people:id/5 -> ./people/Darrel
Linked ./people/:state:id/MI/5 -> ./people/Darrel
+ cargo run --quiet -- get -a ./people
user.ghee.tableinfo {"abs_path":"/home/josh/Projects/Ghee/example/people","key":"name","indices":{"id":"/home/josh/Projects/Ghee/example/people:id","name":".","state,id":"./:state:id"},"indices_abs":{"id":"/home/josh/Projects/Ghee/example/people:id","name":"/home/josh/Projects/Ghee/example/people","state,id":"/home/josh/Projects/Ghee/example/people/:state:id"},"indices_both":{"id":{"rel":"/home/josh/Projects/Ghee/example/people:id","abs":"/home/josh/Projects/Ghee/example/people:id"},"name":{"rel":".","abs":"/home/josh/Projects/Ghee/example/people"},"state,id":{"rel":"./:state:id","abs":"/home/josh/Projects/Ghee/example/people/:state:id"}}}
user.ghee.tableinfo {"key":"name","indices_abs":{"id":"/home/josh/Projects/Ghee/example/people:id","name":"/home/josh/Projects/Ghee/example/people","state,id":"/home/josh/Projects/Ghee/example/people/:state:id"}}
./people/Darrel user.id 5
./people/Darrel user.name Darrel
./people/Darrel user.state MI

View file

@ -2,7 +2,7 @@ use anyhow::Result;
use ghee_lang::{Key, Predicate, Value};
use thiserror::Error;
use crate::{table_info, walk::walk_records, xattr_values, Record};
use crate::{paths::PathBufExt, table_info, walk::walk_records, xattr_values, Record};
use std::{collections::BTreeMap, fs::remove_file, path::PathBuf};
@ -59,7 +59,8 @@ fn unlink_record(
remove_file(&record_path).map_err(DelErr::IoError)?;
if verbose {
eprintln!("Removed {}", record_path.display());
let record_path_display = record_path.relative_to_curdir_if_possible();
eprintln!("Removed {}", record_path_display.display());
}
}
@ -159,10 +160,10 @@ mod test {
{
let indices1 = table_info(&s.dir1).unwrap().unwrap();
assert_eq!(indices1.indices().len(), 2);
assert_eq!(indices1.indices_abs().len(), 2);
let indices2 = table_info(&s.dir2).unwrap().unwrap();
assert_eq!(indices2.indices().len(), 2);
assert_eq!(indices2.indices_abs().len(), 2);
}
del(&s.dir1, &vec![], &vec![Value::Number(0f64)], true).unwrap();

View file

@ -130,9 +130,9 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert!(info1.indices().contains_key(&key1));
assert!(info1.indices_abs().contains_key(&key1));
assert_eq!(info1.index_path_abs(&key1), &dir1.dir);
assert_eq!(info1.indices().len(), 1);
assert_eq!(info1.indices_abs().len(), 1);
}
idx(&dir1, Some(&dir2), &key2, false).unwrap();
@ -140,9 +140,9 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert!(info1.indices().contains_key(&key2));
assert!(info1.indices_abs().contains_key(&key2));
assert_eq!(info1.index_path_abs(&key2), &dir2.dir);
assert_eq!(info1.indices().len(), 2);
assert_eq!(info1.indices_abs().len(), 2);
assert_eq!(info1.key(), &key1);
}
@ -150,9 +150,9 @@ mod test {
{
let info2 = table_info(&dir2).unwrap().unwrap();
assert!(info2.indices().contains_key(&key1));
assert!(info2.indices_abs().contains_key(&key1));
assert_eq!(info2.index_path_abs(&key1), &dir1.dir);
assert_eq!(info2.indices().len(), 2);
assert_eq!(info2.indices_abs().len(), 2);
assert_eq!(info2.key(), &key2);
}
@ -162,7 +162,7 @@ mod test {
// Make sure the indices are updated properly after a second index
// (no overwriting of the previous)
let info = table_info(&dir1).unwrap().unwrap();
let indices = info.indices();
let indices = info.indices_abs();
assert_eq!(indices.len(), 3);
assert!(indices.contains_key(&key1));
@ -193,7 +193,7 @@ mod test {
for key in vec![&key1, &key2, &key3] {
assert!(
info.indices().contains_key(key),
info.indices_abs().contains_key(key),
"Table at {} does not contain key {:?}",
dir.display(),
key

View file

@ -2,7 +2,6 @@ use std::path::PathBuf;
use anyhow::Result;
use ghee_lang::Key;
use path_absolutize::Absolutize;
use thiserror::Error;
use crate::{set_table_info, table_info, GheeErr, TableInfo};
@ -21,8 +20,6 @@ pub fn init(dir: &PathBuf, key: &Key, verbose: bool) -> Result<()> {
return Err(GheeErr::WontInitializeToEmptyKey.into());
}
let dir = dir.absolutize().unwrap().to_path_buf();
let prior_info = table_info(&dir)?;
if prior_info.is_none() {

View file

@ -8,10 +8,7 @@ use anyhow::Result;
use ghee_lang::{Key, Value, Xattr};
use thiserror::Error;
use crate::{
paths::{PathBufExt, PathBufs},
table_info, Record,
};
use crate::{paths::PathBufExt, table_info, Record};
#[derive(Error, Debug)]
pub enum InsErr {
@ -109,33 +106,24 @@ pub fn ins_records(
for record in records {
// The paths within indices where this record would be located, if any
let paths: Vec<PathBufs> = {
//
// Tuple is (table_path, record_path)
let paths: Vec<(&PathBuf, PathBuf)> = {
// Put shorter-pathed indices first
let ordered = {
let mut ordered: Vec<(&Key, &PathBufs)> = info.indices_both().iter().collect();
ordered.sort_by_key(|(_k, p)| p.abs.to_string_lossy().len());
let mut ordered: Vec<(&Key, &PathBuf)> = info.indices_abs().iter().collect();
ordered.sort_by_key(|(_k, p)| p.to_string_lossy().len());
ordered
};
ordered
.into_iter()
.map(|(key, path)| {
let table_path = path.abs.relative_to_curdir_if_possible();
let abs_record_path = key.path_for_record(path.abs.clone(), &record);
let maybe_rel_record_path = path
.rel
.as_ref()
.map(|rel| key.path_for_record(rel.clone(), &record));
(table_path, maybe_rel_record_path, abs_record_path)
})
.filter(|(_table_path, _maybe_rel, abs)| abs.is_ok())
.map(|(table_path, maybe_rel, abs)| {
PathBufs::new(
maybe_rel.map(|rel| rel.unwrap().resolve_curdir_if_possible(table_path)),
abs.unwrap(),
)
.map(|(key, table_path)| {
let record_path = key.path_for_record(table_path.clone(), &record);
(table_path, record_path)
})
.filter(|(_table_path, record_path)| record_path.is_ok())
.map(|(table_path, record_path)| (table_path, record_path.unwrap()))
.collect()
};
@ -143,42 +131,44 @@ pub fn ins_records(
return Err(InsErr::IncludedInNoIndex.into());
}
let record_path = &paths[0];
let (_index_path, record_path) = &paths[0];
create_dir_all(record_path.abs.parent().unwrap()).map_err(|err| InsErr::IoError {
create_dir_all(record_path.parent().unwrap()).map_err(|err| InsErr::IoError {
err,
path: record_path.abs.parent().unwrap().into(),
path: record_path.parent().unwrap().into(),
})?;
File::create(&record_path.abs).map_err(|err| InsErr::IoError {
File::create(&record_path).map_err(|err| InsErr::IoError {
err,
path: record_path.abs.clone(),
path: record_path.clone(),
})?;
write_record_as_xattrs(&record_path.abs, &record)?;
write_record_as_xattrs(&record_path, &record)?;
let record_path_display = record_path.relative_to_curdir_if_possible();
if verbose {
eprintln!("Initialized {}", record_path.rel_else_abs().display());
eprintln!("Initialized {}", record_path_display.display());
}
// Hardlink to all other indices
for dest in paths.iter().skip(1) {
create_dir_all(dest.abs.parent().unwrap()).map_err(|err| InsErr::IoError {
for (_dest_index_path, dest_record_path) in paths.iter().skip(1) {
create_dir_all(dest_record_path.parent().unwrap()).map_err(|err| InsErr::IoError {
err,
path: dest.abs.parent().unwrap().into(),
path: dest_record_path.parent().unwrap().into(),
})?;
hard_link(&record_path.abs, &dest.abs).map_err(|err| InsErr::IoError {
hard_link(&record_path, &dest_record_path).map_err(|err| InsErr::IoError {
err,
path: record_path.abs.clone(),
path: record_path.clone(),
})?;
if verbose {
let dest_record_path_display = dest_record_path.relative_to_curdir_if_possible();
eprintln!(
"Linked {} -> {}",
dest.rel_else_abs().display(),
record_path.rel_else_abs().display()
dest_record_path_display.display(),
record_path_display.display()
);
}
}

View file

@ -57,7 +57,7 @@ pub fn rm(
}
if let Some(table_info) = table_info.as_ref() {
for key in table_info.indices().keys() {
for key in table_info.indices_abs().keys() {
if key.subkeys.contains(field) && xattrs.contains_key(field) {
exited_indices.insert(key.clone());
}

View file

@ -56,7 +56,7 @@ pub fn set<P: AsRef<Path>>(
let old_paths: Option<HashSet<PathBuf>> = table_info.as_ref().map(|table_info| {
let old_record = old_record.as_ref().unwrap();
table_info
.indices()
.indices_abs()
.iter()
.filter_map(|(key, path)| key.path_for_record(path.clone(), old_record).ok())
.collect()
@ -89,7 +89,7 @@ pub fn set<P: AsRef<Path>>(
let new_paths: Option<HashSet<PathBuf>> = table_info.as_ref().map(|table_info| {
let new_record = new_record.as_ref().unwrap();
table_info
.indices()
.indices_abs()
.iter()
.map(|(key, path)| key.path_for_record(path.clone(), new_record).unwrap())
.collect()

View file

@ -17,18 +17,7 @@ pub fn status(path: &PathBuf, sort: bool) -> Result<()> {
println!("status of {}", path.display());
println!("curdir: {}", current_dir().unwrap().display());
if let Some(table_info) = containing_table_info(&path)? {
let table_path = {
let table_info_path = table_info.path();
if table_info_path.has_root() {
table_info_path.clone()
} else {
let mut p = path.clone().parent().unwrap().to_path_buf();
p.push(table_info_path);
p.absolutize().unwrap().to_path_buf()
}
};
let table_path = table_info.path_abs();
println!("table_path: {}", table_path.display());
// Empty list of predicate clauses to hand out

View file

@ -25,7 +25,7 @@ pub fn touch(path: &PathBuf, create_parents: bool) -> Result<()> {
File::create(&path)?;
if let Some(info) = containing_table_info(&path)? {
let xattrs = xattr_values_from_path(info.key(), info.abs_path(), &path)?;
let xattrs = xattr_values_from_path(info.key(), info.path_abs(), &path)?;
write_xattr_values(path, &xattrs)?;
}

View file

@ -8,7 +8,6 @@ mod tableinfo;
mod test_support;
pub mod walk;
use paths::PathBufExt;
pub use tableinfo::*;
use std::{
@ -326,10 +325,8 @@ pub fn declare_closure_indices<P1: AsRef<Path>, P2: AsRef<Path>>(dir1: P1, dir2:
let info2 = table_info(&dir2)?
.ok_or_else(|| IndexListPushErr::TargetTableInfoNotFound(dir2.as_ref().into()))?;
for path1 in info1.indices().values() {
let path1 = path1.resolve_curdir_if_possible(info1.abs_path());
for path2 in info2.indices().values() {
let path2 = path2.resolve_curdir_if_possible(info2.abs_path());
for path1 in info1.indices_abs().values() {
for path2 in info2.indices_abs().values() {
declare_indices(&path1, path2)?;
}
}
@ -405,7 +402,7 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 1);
assert_eq!(info1.indices_abs().len(), 1);
}
let dir2 = TempDirAuto::new("ghee-test-index-list-push:2");
@ -416,11 +413,11 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 1);
assert_eq!(info1.indices_abs().len(), 1);
let info2 = table_info(&dir2).unwrap().unwrap();
assert_eq!(info2.key(), &key2);
assert_eq!(info2.indices().len(), 1);
assert_eq!(info2.indices_abs().len(), 1);
}
index_list_push(&dir1, &dir2).unwrap();
@ -428,11 +425,11 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 2);
assert_eq!(info1.indices_abs().len(), 2);
let info2 = table_info(&dir2).unwrap().unwrap();
assert_eq!(info2.key(), &key2);
assert_eq!(info2.indices().len(), 1);
assert_eq!(info2.indices_abs().len(), 1);
}
}
@ -447,7 +444,7 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 1);
assert_eq!(info1.indices_abs().len(), 1);
}
let dir2 = TempDirAuto::new("ghee-test-index-list-push:2");
@ -458,11 +455,11 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 1);
assert_eq!(info1.indices_abs().len(), 1);
let info2 = table_info(&dir2).unwrap().unwrap();
assert_eq!(info2.key(), &key2);
assert_eq!(info2.indices().len(), 1);
assert_eq!(info2.indices_abs().len(), 1);
}
declare_indices(&dir1, &dir2).unwrap();
@ -470,11 +467,11 @@ mod test {
{
let info1 = table_info(&dir1).unwrap().unwrap();
assert_eq!(info1.key(), &key1);
assert_eq!(info1.indices().len(), 2);
assert_eq!(info1.indices_abs().len(), 2);
let info2 = table_info(&dir2).unwrap().unwrap();
assert_eq!(info2.key(), &key2);
assert_eq!(info2.indices().len(), 2);
assert_eq!(info2.indices_abs().len(), 2);
}
}

View file

@ -19,6 +19,11 @@ pub enum RelativizeErr {
}
pub trait PathBufExt {
fn relative_to_curdir(&self) -> Result<PathBuf> {
let cur = current_dir().unwrap();
self.relativize(cur)
}
/// Relativize relative to the current directory
fn relative_to_curdir_if_possible(&self) -> PathBuf {
let cur = current_dir().unwrap();
@ -97,11 +102,13 @@ impl PathBufExt for PathBuf {
pub struct PathBufs {
pub rel: Option<PathBuf>,
pub abs: PathBuf,
pub curdir: PathBuf,
}
impl PathBufs {
pub fn new(rel: Option<PathBuf>, abs: PathBuf) -> Self {
debug_assert!(abs.is_absolute());
Self { rel, abs }
let curdir = abs.relative_to_curdir().unwrap();
Self { rel, abs, curdir }
}
pub fn rel_else_abs(&self) -> &PathBuf {

View file

@ -10,10 +10,7 @@ use path_absolutize::Absolutize;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{
paths::{PathBufExt, PathBufs},
XATTR_TABLE_INFO,
};
use crate::XATTR_TABLE_INFO;
#[derive(Error, Debug)]
enum TableInfoErr {
@ -29,11 +26,9 @@ enum TableInfoErr {
#[derive(Clone, Serialize, Deserialize)]
pub struct TableInfo {
abs_path: PathBuf,
key: Key,
indices: BTreeMap<Key, PathBuf>,
indices_abs: BTreeMap<Key, PathBuf>,
indices_both: BTreeMap<Key, PathBufs>,
}
impl TableInfo {
/// Initialize a new TableInfo for a table with primary key `key` located at path `path`.
@ -41,50 +36,25 @@ impl TableInfo {
/// Alternate indices should be added subsequently using `add_index`
pub fn new<P: AsRef<Path>>(key: Key, path: P) -> Self {
let abs_path = path.as_ref().absolutize().unwrap().to_path_buf();
let rel_path = PathBuf::from(".");
let mut indices = BTreeMap::new();
indices.insert(key.clone(), rel_path.clone());
let mut indices_abs = BTreeMap::new();
indices_abs.insert(key.clone(), abs_path.clone());
let mut indices_both = BTreeMap::new();
indices_both.insert(key.clone(), PathBufs::new(Some(rel_path), abs_path.clone()));
Self {
abs_path,
key,
indices,
indices_abs,
indices_both,
}
}
pub fn abs_path(&self) -> &PathBuf {
&self.abs_path
Self { key, indices_abs }
}
pub fn key(&self) -> &Key {
&self.key
}
pub fn path(&self) -> &PathBuf {
&self.indices[&self.key]
pub fn path_abs(&self) -> &PathBuf {
&self.indices_abs[&self.key]
}
pub fn add_index(&mut self, key: Key, path: PathBuf) {
let relpath = path.relativize_if_possible(&self.abs_path);
self.indices.insert(key.clone(), relpath.clone());
let abs_path = path.absolutize().unwrap().to_path_buf();
debug_assert!(abs_path.is_absolute());
self.indices_abs.insert(key.clone(), abs_path.clone());
self.indices_both
.insert(key.clone(), PathBufs::new(Some(relpath), abs_path));
}
pub fn indices(&self) -> &BTreeMap<Key, PathBuf> {
&self.indices
}
pub fn indices_abs(&self) -> &BTreeMap<Key, PathBuf> {
@ -92,21 +62,9 @@ impl TableInfo {
&self.indices_abs
}
pub fn indices_both(&self) -> &BTreeMap<Key, PathBufs> {
&self.indices_both
}
pub fn index_path(&self, key: &Key) -> &PathBuf {
&self.indices[key]
}
pub fn index_path_abs(&self, key: &Key) -> &PathBuf {
&self.indices_abs[key]
}
pub fn index_path_both(&self, key: &Key) -> &PathBufs {
&self.indices_both[key]
}
}
/**
@ -136,8 +94,8 @@ pub fn table_info<P: AsRef<Path>>(dir: P) -> Result<Option<TableInfo>> {
let info: TableInfo =
serde_json::from_slice(raw.as_slice()).map_err(TableInfoErr::JsonError)?;
debug_assert!(info.indices.contains_key(&info.key));
debug_assert_eq!(info.path(), Path::new("."));
debug_assert!(info.indices_abs.contains_key(&info.key));
debug_assert_eq!(info.path_abs(), &dir.absolutize().unwrap().to_path_buf());
Ok(Some(info))
}
@ -248,7 +206,7 @@ mod test {
{
let info = table_info(&dir1).unwrap().unwrap();
assert!(info.indices.contains_key(&key1));
assert!(info.indices_abs.contains_key(&key1));
}
for dir in vec![&dir2, &dir3] {
@ -319,7 +277,7 @@ mod test {
for dir in vec![&dir1, &dir2, &dir3, &file] {
let info = containing_table_info(dir).unwrap().unwrap();
assert_eq!(info.key, key1);
assert!(info.indices.contains_key(&key1));
assert!(info.indices_abs.contains_key(&key1));
}
assert!(

View file

@ -25,7 +25,7 @@ pub struct PathVisit<'a, 'b, 'c> {
impl<'a, 'b, 'c> PathVisit<'a, 'b, 'c> {
pub fn is_table_root(&self) -> bool {
if let Some(table_info) = self.table_info {
let table_path = table_info.index_path(table_info.key());
let table_path = table_info.index_path_abs(table_info.key());
table_path == self.path
} else {
false
@ -45,24 +45,6 @@ impl<'a, 'b, 'c> PathVisit<'a, 'b, 'c> {
}
/// The path by which this record is reachable in the original table/index targeted
pub fn original_path(&self) -> Result<PathBuf> {
if let Some(table_info) = self.table_info {
let original_table_key = table_info.key();
let subkey_values = original_table_key.value_for_record(self.xattr_values)?;
let mut path = table_info.index_path(table_info.key()).clone();
for sub in subkey_values {
path.push(sub.to_string());
}
Ok(path)
} else {
// If there are no indices, this path itself is the original path
Ok(self.path.clone())
}
}
pub fn original_path_abs(&self) -> Result<PathBuf> {
if let Some(table_info) = self.table_info {
let original_table_key = table_info.key();
@ -101,7 +83,7 @@ pub(crate) fn walk_paths<'a, 'b>(
let abs_path = path.absolutize().unwrap().to_path_buf();
let (key, base_path, traverse_path) = if let Some(table_info) = containing_table_info(path)? {
if table_info.abs_path() == &abs_path {
if table_info.path_abs() == &abs_path {
// `path` is an initialized table
let (best_key, best_path) =
best_index(table_info.indices_abs(), where_, table_info.key());
@ -111,7 +93,7 @@ pub(crate) fn walk_paths<'a, 'b>(
// whether it's a file or a directory, we will traverse from there
(
table_info.key().clone(),
table_info.abs_path().clone(),
table_info.path_abs().clone(),
abs_path.clone(),
)