Work toward recursive set and rm; failing tests

This commit is contained in:
Josh Hansen 2023-09-07 18:15:27 -07:00
parent 6e5f4345d4
commit b9ef10bf6e
6 changed files with 173 additions and 27 deletions

View file

@ -62,6 +62,8 @@ enum Commands {
paths: Vec<PathBuf>,
#[arg(name = "field", short, long, help = "xattrs to remove")]
fields: Vec<Xattr>,
#[arg(long, help = "Process paths nonrecursively; defaults to false")]
flat: bool,
#[arg(short, long)]
verbose: bool,
},
@ -94,6 +96,8 @@ enum Commands {
paths: Vec<PathBuf>,
#[arg(name = "set", short, long, help = "k=v pairs to set", value_parser = ValueParser::new(AssignmentParser{}))]
field_assignments: Vec<Assignment>,
#[arg(long, help = "Process paths nonrecursively; defaults to false")]
flat: bool,
#[arg(short, long)]
verbose: bool,
},
@ -271,9 +275,10 @@ fn run_command(cmd: &Commands) {
Commands::Rm {
paths,
fields,
flat,
verbose,
} => {
rm(paths, fields, *verbose)
rm(paths, fields, !*flat, *verbose)
.unwrap_or_else(|e| panic!("Error removing xattr(s): {}", e));
}
Commands::Get {
@ -290,8 +295,9 @@ fn run_command(cmd: &Commands) {
Commands::Set {
paths,
field_assignments,
flat,
verbose,
} => set(paths, field_assignments, *verbose)
} => set(paths, field_assignments, !*flat, *verbose)
.unwrap_or_else(|e| panic!("Error setting xattrs: {}", e)),
Commands::Ins {

View file

@ -113,6 +113,6 @@ mod test {
create(&dir, &key, false).unwrap();
get(&vec![dir], &vec![], false, &vec![], true, true).unwrap();
get(&vec![dir], &vec![], false, &vec![], true, true, false).unwrap();
}
}

View file

@ -1,5 +1,6 @@
use anyhow::Result;
use thiserror::Error;
use walkdir::WalkDir;
use std::path::PathBuf;
@ -15,27 +16,150 @@ pub enum RmErr {
*
* Not to be mistaken for `del` which removes records from tables.
*/
pub fn rm(paths: &Vec<PathBuf>, fields: &Vec<Xattr>, verbose: bool) -> Result<()> {
pub fn rm(paths: &Vec<PathBuf>, fields: &Vec<Xattr>, recursive: bool, verbose: bool) -> Result<()> {
let max_depth = if recursive { usize::MAX } else { 0 };
if verbose {
let fields: Vec<String> = fields.iter().map(|f| f.to_string()).collect();
eprintln!("Removing {} from:", fields.join(", "));
}
for path in paths {
let fields: Vec<Xattr> = if fields.is_empty() {
list_xattrs(path)
} else {
fields.clone()
};
for entry in WalkDir::new(path).max_depth(max_depth).into_iter() {
let entry = entry?;
for field in fields.iter() {
let field_osstring = field.to_osstring();
xattr::remove(path, field_osstring).map_err(RmErr::IoError)?;
}
if verbose {
eprintln!("\t{}", path.display());
let fields: Vec<Xattr> = if fields.is_empty() {
list_xattrs(path)
} else {
fields.clone()
};
for field in fields.iter() {
let field_osstring = field.to_osstring();
xattr::remove(entry.path(), field_osstring).map_err(RmErr::IoError)?;
}
if verbose {
eprintln!("\t{}", entry.path().display());
}
}
}
Ok(())
}
#[cfg(test)]
mod test {
use crate::{cmd::rm, test_support::Scenario, xattr_values};
#[test]
fn test_rm() {
let s: Scenario = Default::default();
// The path in dir1
let path1 = {
let mut path = s.dir1.clone();
path.push("0");
path
};
let path2 = {
let mut path = s.dir2.clone();
path.push("1");
path.push("2");
path
};
assert!(path1.exists());
assert!(path2.exists());
{
let values1 = xattr_values(&path1).unwrap();
assert!(values1.contains_key(&s.xattr1));
assert!(values1.contains_key(&s.xattr2));
assert!(values1.contains_key(&s.xattr3));
}
{
let values2 = xattr_values(&path2).unwrap();
assert!(values2.contains_key(&s.xattr1));
assert!(values2.contains_key(&s.xattr2));
assert!(values2.contains_key(&s.xattr3));
}
// Remove
rm(&vec![path1.clone()], &vec![s.xattr1.clone()], false, false).unwrap();
assert!(
!path1.exists(),
"Record not removed from index even though it no longer has relevant xattr"
);
assert!(path2.exists());
{
let values2 = xattr_values(path2).unwrap();
assert!(!values2.contains_key(&s.xattr1));
assert!(values2.contains_key(&s.xattr2));
assert!(values2.contains_key(&s.xattr3));
}
}
#[test]
fn test_rm_recursive() {
let s: Scenario = Default::default();
let path1 = {
let mut path = s.dir1.clone();
path.push("0");
path
};
let path2 = {
let mut path = s.dir2.clone();
path.push("1");
path.push("2");
path
};
let path3 = {
let mut path = s.dir1.clone();
path.push("10");
path
};
let path4 = {
let mut path = s.dir2.clone();
path.push("11");
path.push("12");
path
};
assert!(path1.exists());
assert!(path2.exists());
assert!(path3.exists());
assert!(path4.exists());
for path in vec![&path1, &path2, &path3, &path4] {
let values = xattr_values(&path).unwrap();
assert!(values.contains_key(&s.xattr1));
assert!(values.contains_key(&s.xattr2));
assert!(values.contains_key(&s.xattr3));
}
// Remove
rm(&vec![s.dir1.clone()], &vec![s.xattr1.clone()], true, false).unwrap();
assert!(
!path1.exists(),
"Record not removed from index even though it no longer has relevant xattr"
);
assert!(path2.exists());
assert!(
!path3.exists(),
"Record not removed from index even though it no longer has relevant xattr"
);
assert!(path4.exists());
}
}

View file

@ -1,5 +1,6 @@
use anyhow::Result;
use thiserror::Error;
use walkdir::WalkDir;
use crate::parser::value::Value;
@ -19,21 +20,28 @@ pub enum SetErr {
pub fn set<P: AsRef<Path>>(
paths: &Vec<P>,
field_assignments: &Vec<Assignment>,
recursive: bool,
verbose: bool,
) -> Result<()> {
let max_depth = if recursive { usize::MAX } else { 0 };
let field_assignments: Vec<(Xattr, Value)> = field_assignments
.iter()
.map(|fa| (fa.xattr.clone(), fa.value.clone()))
.collect();
for path in paths {
for (field, value) in field_assignments.iter() {
let field_osstring = field.to_osstring();
xattr::set(path, field_osstring, value.as_bytes().as_slice())
.map_err(SetErr::IoError)?;
for entry in WalkDir::new(&path).max_depth(max_depth).into_iter() {
let entry = entry?;
if verbose {
eprintln!("Set {} to {} on {}", field, value, path.as_ref().display());
for (field, value) in field_assignments.iter() {
let field_osstring = field.to_osstring();
xattr::set(entry.path(), field_osstring, value.as_bytes().as_slice())
.map_err(SetErr::IoError)?;
if verbose {
eprintln!("Set {} to {} on {}", field, value, path.as_ref().display());
}
}
}
}

View file

@ -601,7 +601,7 @@ mod test {
assert_eq!(get_key(&dir).unwrap().unwrap(), key);
}
fn visited(path: &PathBuf, recursive: bool, all: bool) -> Vec<PathBuf> {
fn visited(path: &PathBuf, recursive: bool, all: bool, nosort: bool) -> Vec<PathBuf> {
let visited: Rc<RefCell<Vec<PathBuf>>> = Rc::new(RefCell::new(Vec::new()));
walk(
@ -609,6 +609,7 @@ mod test {
&Vec::new(),
recursive,
all,
nosort,
&|path| {
visited.borrow_mut().push(path.path.clone());
Ok(())
@ -624,7 +625,7 @@ mod test {
fn test_walk_bare_dir() {
let dir = TempDir::new("ghee-test-walk").unwrap().into_path();
assert!(visited(&dir, true, true).is_empty());
assert!(visited(&dir, true, true, false).is_empty());
}
#[test]
@ -633,7 +634,7 @@ mod test {
init(&dir, &Key::from_string("test"), false).unwrap();
let visited = visited(&dir, true, true);
let visited = visited(&dir, true, true, false);
assert_eq!(
visited.len(),
@ -650,7 +651,7 @@ mod test {
init(&dir, &Key::from_string("test"), false).unwrap();
let visited = visited(&dir, true, false);
let visited = visited(&dir, true, false, true);
assert!(visited.is_empty());
}
@ -663,6 +664,7 @@ mod test {
&vec![&dir1],
&vec![parse_assignment(b"a=1").unwrap().1],
false,
false,
)
.unwrap();
@ -675,15 +677,16 @@ mod test {
&vec![&dir2],
&vec![parse_assignment(b"a=2").unwrap().1],
false,
false,
)
.unwrap();
let visited_flat = visited(&dir1, false, false);
let visited_flat = visited(&dir1, false, false, true);
assert_eq!(visited_flat.len(), 1);
assert!(visited_flat.contains(&dir1));
let visited_recursive = visited(&dir1, true, false);
let visited_recursive = visited(&dir1, true, false, false);
assert_eq!(visited_recursive.len(), 2);
assert!(visited_recursive.contains(&dir1));

View file

@ -7,6 +7,11 @@ use crate::{
parser::{key::Key, xattr::Xattr},
};
/** A test scenario
*
* dir1 is indexed by ['test1'] (xattr1)
* dir2 is indexed by ['test2', 'test3'] (xattr2, xattr3)
*/
pub struct Scenario {
/// Table indexed by xattr1
pub dir1: PathBuf,