diff --git a/src/fs.rs b/src/fs.rs index 3a4e162d1..1e45ee7c2 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1033,6 +1033,11 @@ impl ReadDirAllocation { } } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct DirIterationTell { + tell_result: u32, +} + pub struct ReadDir<'a, 'b, S: driver::Storage> { alloc: RefCell<&'b mut ReadDirAllocation>, fs: &'b Filesystem<'a, S>, @@ -1040,6 +1045,67 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> { path: PathBuf, } +impl<'a, 'b, S: driver::Storage> ReadDir<'a, 'b, S> { + /// Return the position of the directory + /// + /// The returned offset is only meant to be consumed by seek and may not make + /// sense, but does indicate the current position in the directory iteration. + /// + /// Returns the position of the directory, which can be returned to using [`seek`](Self::seek). + pub fn tell(&self) -> Result { + let value = unsafe { + ll::lfs_dir_tell( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + ) + }; + if value < 0 { + Err(io::result_from((), value).unwrap_err()) + } else { + Ok(DirIterationTell { + tell_result: value as u32, + }) + } + } + + /// Change the position of the directory + /// + /// The new off must be a value previous returned from [`tell`](Self::tell) and specifies + /// an absolute offset in the directory seek. + pub fn seek(&mut self, state: DirIterationTell) -> Result { + let value = unsafe { + ll::lfs_dir_seek( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + state.tell_result, + ) + }; + if value < 0 { + Err(io::result_from((), value).unwrap_err()) + } else { + Ok(DirIterationTell { + tell_result: value as u32, + }) + } + } + + /// Change the position of the directory to the beginning of the directory + pub fn rewind(&mut self) -> Result<()> { + let res = unsafe { + ll::lfs_dir_rewind( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + ) + }; + + if res < 0 { + Err(io::result_from((), res).unwrap_err()) + } else { + Ok(()) + } + } +} + impl<'a, 'b, S: driver::Storage> Iterator for ReadDir<'a, 'b, S> { type Item = Result; diff --git a/src/tests.rs b/src/tests.rs index 5159d6ceb..e4c28d9ca 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -480,12 +480,16 @@ fn test_iter_dirs() { file.set_len(37)?; fs.create_file_and_then(b"/tmp/file.b\0".try_into()?, |file| file.set_len(42)) })?; + let mut tells = Vec::new(); - fs.read_dir_and_then(b"/tmp\0".try_into()?, |dir| { + fs.read_dir_and_then(b"/tmp\0".try_into()?, |mut dir| { let mut found_files: usize = 0; let mut sizes = [0usize; 4]; + let mut i = 0; - for (i, entry) in dir.enumerate() { + tells.push(dir.tell()?); + while let Some(entry) = dir.next() { + tells.push(dir.tell()?); let entry = entry?; // assert_eq!(entry.file_name(), match i { @@ -498,13 +502,31 @@ fn test_iter_dirs() { sizes[i] = entry.metadata().len(); found_files += 1; + i += 1; } assert_eq!(sizes, [0, 0, 37, 42]); assert_eq!(found_files, 4); + for (i, tell) in tells.iter().enumerate() { + dir.rewind().unwrap(); + let mut found_files: usize = 0; + let mut sizes = Vec::new(); + dir.seek(*tell)?; + + for entry in &mut dir { + let entry = entry?; + sizes.push(entry.metadata().len()); + found_files += 1; + } + + assert_eq!(sizes, [0, 0, 37, 42][i..]); + assert_eq!(found_files, 4 - i); + } Ok(()) }) + .unwrap(); + Ok(()) }) .unwrap(); }