From 9ca88a1ecdad6c492e1d07f7fff77e199a5fefd0 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 24 Nov 2017 18:45:37 -0800 Subject: import/export drive directories --- src/bin/geniza-drive.rs | 31 ++++++++++++++++++ src/drive.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) (limited to 'src') diff --git a/src/bin/geniza-drive.rs b/src/bin/geniza-drive.rs index d402539..42e06e2 100644 --- a/src/bin/geniza-drive.rs +++ b/src/bin/geniza-drive.rs @@ -51,6 +51,18 @@ fn run() -> Result<()> { .arg_from_usage(" 'file to export'") .arg_from_usage("--target 'path to save the file to (if not same name)'") ) + .subcommand( + SubCommand::with_name("import-dir") + .about("Adds a directory (recursively) to the dat") + .arg_from_usage(" 'directory to add'") + .arg_from_usage("--target 'path to import the file to (if not top level)'") + ) + .subcommand( + SubCommand::with_name("export-dir") + .about("Copies a directory (recursively) from dat archive to local disk") + .arg_from_usage(" 'directory to export'") + .arg_from_usage("--target 'path to save the directory to (if not same name)'") + ) .subcommand( SubCommand::with_name("log") .about("History of additions/deletions from this dat") @@ -122,6 +134,25 @@ fn run() -> Result<()> { }; drive.export_file(&path, &fpath)?; } + ("import-dir", Some(subm)) => { + let path = Path::new(subm.value_of("DIR").unwrap()); + let mut drive = DatDrive::open(dir, true)?; + let fpath = match subm.value_of("target") { + None => Path::new("/").join(path.file_name().unwrap()), + Some(p) => Path::new("/").join(p) + }; + drive.import_dir(&path, &fpath)?; + + } + ("export-dir", Some(subm)) => { + let path = Path::new(subm.value_of("DIR").unwrap()); + let mut drive = DatDrive::open(dir, true)?; + let fpath = match subm.value_of("target") { + None => Path::new("/").join(path.file_name().unwrap()), + Some(p) => Path::new("/").join(p) + }; + drive.export_dir(&path, &fpath)?; + } ("log", Some(_subm)) => { let mut drive = DatDrive::open(dir, false)?; for entry in drive.history(0) { diff --git a/src/drive.rs b/src/drive.rs index a4e6cf9..360555b 100644 --- a/src/drive.rs +++ b/src/drive.rs @@ -464,6 +464,56 @@ impl<'a> DatDrive { Ok(()) } + /// Copies Stat metadata and all content from a directory (recursively) from the "real" + /// filesystem into the DatDrive. + /// On success, returns version number including all the added files. + pub fn import_dir, Q: AsRef>(&mut self, source: P, dest: Q) -> Result { + let source = source.as_ref(); + let dest = dest.as_ref(); + // TODO: check that dest doesn't exist (or is directory) + let nearest = self.get_nearest(dest)?; + if let Some(nearest) = nearest { + if nearest.path == dest { + bail!("destination already exists (as a file)"); + } + } + let mut ret = self.entry_count()?; + if source.is_dir() { + for entry in read_dir(source)? { + let entry = entry?; + let path = entry.path(); + let fname = path.file_name().unwrap().to_owned(); + if fname.to_str() == Some(".dat") { + // Don't import yourself! + continue + } + if path.is_dir() { + ret = self.import_dir(path, dest.join(fname))?; + } else { + ret = self.import_file(path, dest.join(fname))?; + } + } + } else { + bail!("Source path wasn't a directory"); + } + Ok(ret) + } + + /// Copies a file from the drive to the "real" filesystem, preserving Stat metadata. + pub fn export_dir, Q: AsRef>(&mut self, source: P, dest: Q) -> Result<()> { + let source = source.as_ref(); + let dest = dest.as_ref(); + // TODO: this collect() is inefficient; read doesn't mutate, so shouldn't really need a + // mutable borrow + let path_list: Vec> = self.read_dir_recursive(source).collect(); + for entry in path_list { + let path = entry?.path.to_owned(); + let out_path = dest.join(path.strip_prefix(source).unwrap()); + self.export_file(path, out_path)?; + } + Ok(()) + } + pub fn read_file_bytes>(&mut self, path: P) -> Result> { let de = self.get_file_entry(path.as_ref())?; if let Some(entry) = de { @@ -702,6 +752,41 @@ fn test_dd_export_file() { assert!(dd.export_file("/z", tmp_dir.path().join("never-created")).is_err()); } +#[test] +fn test_dd_import_dir() { + + use tempdir::TempDir; + use env_logger; + env_logger::init().unwrap(); + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_dir("test-data/dat/tree/Animalia/", "/").unwrap(); + + assert_eq!(dd.read_dir("/").count(), 0); + assert_eq!(dd.read_dir_recursive("/").count(), 2); + + dd.import_file("test-data/dat/alphabet/a", "/a").unwrap(); + assert!(dd.import_dir("test-data/dat/tree/Animalia/", "/a/").is_err()); + +} + +#[test] +fn test_dd_export_dir() { + + use tempdir::TempDir; + //use env_logger; + //env_logger::init().unwrap(); + let tmp_dir = TempDir::new("geniza-test").unwrap(); + let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); + + dd.import_dir("test-data/dat/tree/Animalia/", "/").unwrap(); + + dd.export_dir("/", tmp_dir.path()).unwrap(); + dd.export_dir("/Chordata/Mammalia/Carnivora/Caniformia/", tmp_dir.path()).unwrap(); + //assert!(dd.export_dir("/Fruit/", tmp_dir.path()).is_err()); +} + #[test] fn test_dd_remove_file() { -- cgit v1.2.3