From 58a0dc3387cfc88991bd93fad6d16a84bd7e4902 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 29 Oct 2017 13:59:47 -0700 Subject: progress on drive: basic history dump (un-pretty output) --- src/bin/geniza-drive.rs | 70 ++++++++++++++++++++++++++++++++ src/drive.rs | 106 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 145 insertions(+), 31 deletions(-) create mode 100644 src/bin/geniza-drive.rs (limited to 'src') diff --git a/src/bin/geniza-drive.rs b/src/bin/geniza-drive.rs new file mode 100644 index 0000000..05c7fb7 --- /dev/null +++ b/src/bin/geniza-drive.rs @@ -0,0 +1,70 @@ +// Free Software under GPL-3.0, see LICENSE +// Copyright 2017 Bryan Newbold + +#[macro_use] +extern crate clap; +extern crate env_logger; +#[macro_use] +extern crate error_chain; +extern crate geniza; + +// TODO: more careful import +use geniza::*; +use std::path::Path; +use clap::{App, Arg, SubCommand}; + +fn run() -> Result<()> { + env_logger::init().unwrap(); + + let matches = App::new("geniza-drive") + .version(env!("CARGO_PKG_VERSION")) + // TODO: dat-dir for all commands up here, and have a default vaule ('./dat') + .arg(Arg::with_name("dat-dir") + .short("d") + .long("dat-dir") + .value_name("PATH") + .help("dat drive directory") + .default_value(".dat") // TODO: needs to be default_value_os? + .takes_value(true)) + .subcommand( + SubCommand::with_name("ls") + .about("Lists current files in this dat") + ) + .subcommand( + SubCommand::with_name("log") + .about("History of additions/deletions from this dat") + ) + .get_matches(); + +/* + mode: ::std::option::Option, + uid: ::std::option::Option, + gid: ::std::option::Option, + size: ::std::option::Option, + blocks: ::std::option::Option, + offset: ::std::option::Option, + byteOffset: ::std::option::Option, + mtime: ::std::option::Option, + ctime: ::std::option::Option, +*/ + + let dir = Path::new(matches.value_of("dat-dir").unwrap()); + match matches.subcommand() { + ("ls", Some(_subm)) => { + } + ("log", Some(_subm)) => { + let mut drive = DatDrive::open(dir, false)?; + for entry in drive.history(1) { + println!("{:?}", entry?); + } + } + _ => { + println!("Missing or unimplemented command!"); + println!("{}", matches.usage()); + ::std::process::exit(-1); + } + } + Ok(()) +} + +quick_main!(run); diff --git a/src/drive.rs b/src/drive.rs index 179bebb..7369fb2 100644 --- a/src/drive.rs +++ b/src/drive.rs @@ -1,12 +1,12 @@ -use std::io::{Read, Write}; +use std::io::Read; use std::path::Path; -use protobuf::Message; +//XXX: use protobuf::Message; use protobuf::parse_from_bytes; use errors::*; use sleep_register::*; -use metadata_msgs::{Index, Stat, Node}; +use metadata_msgs::{Stat, Node}; /// "Sort of" follows rust std::fs API for file system access. pub struct DatDrive { @@ -19,53 +19,76 @@ impl DatDrive { /// Instantiates a drive in the given directory. Path should be the complete path (eg, ending /// in '/.dat/'), not an enclosing directory containing files. pub fn create>(path: P) -> Result { + // Calculate content discovery key and write as Index entry in metadata register unimplemented!() } /// Path should be the complete path (eg, ending in '/.dat/'), not an enclosing directory /// containing files. - pub fn open>(path: P) -> Result { - unimplemented!() + pub fn open>(path: P, writable: bool) -> Result { + let mdrive = SleepDirRegister::open(path.as_ref(), "metadata", writable)?; + if mdrive.len()? == 0 { + bail!("Expected at least one entry (Index) in metadata register"); + } + let cdrive = SleepDirRegister::open(path.as_ref(), "content", writable)?; + Ok(DatDrive { + metadata: mdrive, + content: cdrive, + }) } } impl<'a> DatDrive { - pub fn history(start: u64) -> DriveHistory<'a> { + fn find_path(path: &Path) -> Result { unimplemented!() } - pub fn read_dir_recursive>(path: P) -> ReadDriveDir<'a> { + pub fn history<'b>(&'b mut self, start: u64) -> DriveHistory<'b> { + // Start must be at least 1; index 0 is the Index item + let start = if start == 0 { 1 } else { start }; + DriveHistory { + drive: self, + current: start, + } + } + + pub fn read_dir_recursive>(&mut self, path: P) -> ReadDriveDir<'a> { unimplemented!() } - pub fn read_dir>(path: P) -> ReadDriveDir<'a> { + pub fn read_dir>(&mut self, path: P) -> ReadDriveDir<'a> { unimplemented!() } - pub fn file_metadata>(path: P) -> Result { + pub fn file_metadata>(&mut self, path: P) -> Result { unimplemented!() } - pub fn create_file_bytes>(path: P, stat: &Stat, data: &[u8]) -> Result<()> { + pub fn create_file_bytes>(&mut self, path: P, stat: &Stat, data: &[u8]) -> Result<()> { unimplemented!() } - pub fn create_file, R: Read>(path: P, stat: &Stat, source: R) -> Result<()> { + pub fn create_file, R: Read>(&mut self, path: P, stat: &Stat, source: R) -> Result<()> { unimplemented!() } /// Copies Stat metadata and all content from a file in the "real" filesystem into the /// DatDrive. - pub fn import_file, Q: AsRef>(source: P, dest: Q) -> Result<()> { + pub fn import_file, Q: AsRef>(&mut self, source: P, dest: Q) -> Result<()> { + unimplemented!() + } + + /// Copies a file from the drive to the "real" filesystem, preserving Stat metadata. + pub fn export_file, Q: AsRef>(&mut self, source: P, dest: Q) -> Result<()> { unimplemented!() } /* Possible future helper functions to be even more like std::fs - pub fn rename, Q: AsRef>(from: P, to: Q) -> Result<()> - pub fn copy, Q: AsRef>(from: P, to: Q) -> Result<()> - pub fn remove_file>(path: P) -> Result<()> - pub fn remove_dir_all>(path: P) -> Result<()> + pub fn rename, Q: AsRef>(&mut self, from: P, to: Q) -> Result<()> + pub fn copy, Q: AsRef>(&mut self, from: P, to: Q) -> Result<()> + pub fn remove_file>(&mut self, path: P) -> Result<()> + pub fn remove_dir_all>(&mut self, path: P) -> Result<()> */ } @@ -74,12 +97,12 @@ impl<'a> DatDrive { fn test_dd_open() { let mut dd = - DatDrive::open(Path::new("test-data/dat/simple/.dat/")).unwrap(); + DatDrive::open(Path::new("test-data/dat/simple/.dat/"), false).unwrap(); // verified from dat log - assert_eq!(dd.history().collect().len(), 2); - assert_eq!(dd.read_dir().collect().len(), 1); - assert_eq!(dd.read_dir_recurisve().collect().len(), 1); + assert_eq!(dd.history(1).count(), 2); + assert_eq!(dd.read_dir("/").count(), 1); + assert_eq!(dd.read_dir_recursive("/").count(), 1); } #[test] @@ -88,21 +111,49 @@ fn test_dd_create() { let tmp_dir = TempDir::new("geniza-test").unwrap(); let mut dd = DatDrive::create(tmp_dir.path()).unwrap(); - assert_eq!(dd.history().collect().len(), 0); - assert_eq!(dd.read_dir().collect().len(), 0); - assert_eq!(dd.read_dir_recurisve().collect().len(), 0); + assert_eq!(dd.history(1).count(), 0); + assert_eq!(dd.read_dir("/").count(), 0); + assert_eq!(dd.read_dir_recursive("/").count(), 0); } +// TODO: unpack Node into a pub struct +#[derive(Debug)] pub struct DriveEntry { node: Node, index: u64, } +/// Iterator over full drive history (file additions/deletions). pub struct DriveHistory<'a> { drive: &'a mut DatDrive, + current: u64, } -/// Iterator +impl<'a> Iterator for DriveHistory<'a> { + type Item = Result; + fn next(&mut self) -> Option> { + if self.current >= self.drive.metadata.len().unwrap() { + return None; + } + // TODO: handle Err, not unwrap + let data = match self.drive.metadata.get_data_entry(self.current) { + Err(e) => { return Some(Err(e)) }, + Ok(v) => v, + }; + let node = match parse_from_bytes::(&data) { + Err(e) => { return Some(Err(e.into())) }, + Ok(v) => v, + }; + let de = Ok(DriveEntry { + node, + index: self.current, + }); + self.current += 1; + return Some(de); + } +} + +/// Iterator over drive file entries. pub struct ReadDriveDir<'a> { drive: &'a mut DatDrive, recursive: bool, @@ -124,13 +175,6 @@ impl<'a> ReadDriveDir<'a> { } } -impl<'a> Iterator for DriveHistory<'a> { - type Item = DriveEntry; - fn next(&mut self) -> Option { - unimplemented!(); - } -} - impl<'a> Iterator for ReadDriveDir<'a> { type Item = DriveEntry; fn next(&mut self) -> Option { -- cgit v1.2.3