diff --git a/src/consts.rs b/src/consts.rs index c5e00416b..040e436a3 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -12,4 +12,3 @@ pub const FILEBYTES_MAX: u32 = crate::ll::LFS_FILE_MAX as _; pub const ATTRBYTES_MAX: u32 = 1_022; pub type ATTRBYTES_MAX_TYPE = U1022; pub const LOOKAHEADWORDS_SIZE: u32 = 16; - diff --git a/src/driver.rs b/src/driver.rs index 205e0c5f6..fb42103c0 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -4,9 +4,7 @@ use generic_array::ArrayLength; use littlefs2_sys as ll; -use crate::{ - io::Result, -}; +use crate::io::Result; /// Users of this library provide a "storage driver" by implementing this trait. /// @@ -23,7 +21,6 @@ use crate::{ /// Once that's done, we can get rid of `generic-array`s, and replace the /// `*_SIZE` types with `usize`s. pub trait Storage { - // /// Error type for user-provided read/write/erase methods // type Error = usize; diff --git a/src/fs.rs b/src/fs.rs index 666cf6fef..f189488a3 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -11,9 +11,9 @@ use serde::{Deserialize, Serialize}; pub type Bytes = generic_array::GenericArray; use crate::{ + driver, io::{self, Result}, path::{Path, PathBuf}, - driver, }; struct Cache { @@ -53,7 +53,6 @@ impl Default for Allocation { } } impl Allocation { - pub fn new() -> Allocation { let read_size: u32 = Storage::READ_SIZE as _; let write_size: u32 = Storage::WRITE_SIZE as _; @@ -91,9 +90,9 @@ impl Allocation { let filename_max_plus_one: u32 = crate::consts::FILENAME_MAX_PLUS_ONE; debug_assert!(filename_max_plus_one > 1); - debug_assert!(filename_max_plus_one <= 1_022+1); + debug_assert!(filename_max_plus_one <= 1_022 + 1); // limitation of ll-bindings - debug_assert!(filename_max_plus_one == 255+1); + debug_assert!(filename_max_plus_one == 255 + 1); let path_max_plus_one: u32 = crate::consts::PATH_MAX_PLUS_ONE as _; // TODO: any upper limit? debug_assert!(path_max_plus_one >= filename_max_plus_one); @@ -141,7 +140,6 @@ impl Allocation { config, } } - } // pub struct Filesystem<'alloc, 'storage, Storage: driver::Storage> { @@ -157,7 +155,7 @@ pub struct Filesystem<'a, Storage: driver::Storage> { } /// Regular file vs directory -#[derive(Clone,Copy,Debug,Eq,Hash,PartialEq,Serialize,Deserialize)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum FileType { File, Dir, @@ -176,14 +174,13 @@ impl FileType { } /// File type (regular vs directory) and size of a file. -#[derive(Clone,Debug,Eq,PartialEq,Serialize,Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct Metadata { file_type: FileType, size: usize, } -impl Metadata -{ +impl Metadata { pub fn file_type(&self) -> FileType { self.file_type } @@ -205,13 +202,14 @@ impl Metadata } } -impl From for Metadata -{ +impl From for Metadata { fn from(info: ll::lfs_info) -> Self { let file_type = match info.type_ as u32 { ll::lfs_type_LFS_TYPE_DIR => FileType::Dir, ll::lfs_type_LFS_TYPE_REG => FileType::File, - _ => { unreachable!(); } + _ => { + unreachable!(); + } }; Metadata { @@ -221,14 +219,17 @@ impl From for Metadata } } -impl Filesystem<'_, Storage> { +struct RemoveDirAllProgress { + files_removed: usize, + skipped_any: bool, +} +impl Filesystem<'_, Storage> { pub fn allocate() -> Allocation { Allocation::new() } pub fn format(storage: &mut Storage) -> Result<()> { - let alloc = &mut Allocation::new(); let fs = Filesystem::new(alloc, storage); let mut alloc = fs.alloc.borrow_mut(); @@ -254,7 +255,6 @@ impl Filesystem<'_, Storage> { storage: &mut Storage, f: impl FnOnce(&Filesystem<'_, Storage>) -> Result, ) -> Result { - let mut alloc = Allocation::new(); let fs = Filesystem::mount(&mut alloc, storage)?; f(&fs) @@ -279,8 +279,9 @@ impl Filesystem<'_, Storage> { /// So it would seem that there are *at least* the number of blocks returned /// by this method available, at any given time. pub fn available_blocks(&self) -> Result { - let return_code = unsafe { ll::lfs_fs_size( &mut self.alloc.borrow_mut().state) }; - io::result_from(return_code, return_code).map(|blocks| self.total_blocks() - blocks as usize) + let return_code = unsafe { ll::lfs_fs_size(&mut self.alloc.borrow_mut().state) }; + io::result_from(return_code, return_code) + .map(|blocks| self.total_blocks() - blocks as usize) } /// Available number of unused bytes in the filesystem @@ -289,15 +290,14 @@ impl Filesystem<'_, Storage> { /// explained in [`available_blocks`](struct.Filesystem.html#method.available_blocks). /// Second, files may be inlined. pub fn available_space(&self) -> Result { - self.available_blocks().map(|blocks| blocks * Storage::BLOCK_SIZE) + self.available_blocks() + .map(|blocks| blocks * Storage::BLOCK_SIZE) } /// Remove a file or directory. pub fn remove(&self, path: &Path) -> Result<()> { - let return_code = unsafe { ll::lfs_remove( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - ) }; + let return_code = + unsafe { ll::lfs_remove(&mut self.alloc.borrow_mut().state, path.as_ptr()) }; io::result_from((), return_code) } @@ -313,14 +313,21 @@ impl Filesystem<'_, Storage> { self.remove_dir_all_where(path, &|_| true).map(|_| ()) } - #[cfg(feature = "dir-entry-path")] - pub fn remove_dir_all_where

(&self, path: &Path, predicate: &P) -> Result + /// Returns number of deleted files + whether the directory was fully deleted or not + fn remove_dir_all_where_inner

( + &self, + path: &Path, + predicate: &P, + ) -> Result where P: Fn(&DirEntry) -> bool, { if !path.exists(self) { debug_now!("no such directory {}, early return", path); - return Ok(0); + return Ok(RemoveDirAllProgress { + files_removed: 0, + skipped_any: false, + }); } let mut skipped_any = false; let mut files_removed = 0; @@ -343,7 +350,9 @@ impl Filesystem<'_, Storage> { } if entry.file_type().is_dir() { debug_now!("recursing into directory {}", &entry.path()); - files_removed += self.remove_dir_all_where(entry.path(), predicate)?; + let progress = self.remove_dir_all_where_inner(entry.path(), predicate)?; + files_removed += progress.files_removed; + skipped_any |= progress.skipped_any; debug_now!("...back"); } } @@ -354,17 +363,31 @@ impl Filesystem<'_, Storage> { self.remove_dir(path)?; debug_now!("..worked"); } - Ok(files_removed) + Ok(RemoveDirAllProgress { + files_removed, + skipped_any, + }) + } + + #[cfg(feature = "dir-entry-path")] + pub fn remove_dir_all_where

(&self, path: &Path, predicate: &P) -> Result + where + P: Fn(&DirEntry) -> bool, + { + self.remove_dir_all_where_inner(path, predicate) + .map(|progress| progress.files_removed) } /// Rename or move a file or directory. pub fn rename(&self, from: &Path, to: &Path) -> Result<()> { - let return_code = unsafe { ll::lfs_rename( - &mut self.alloc.borrow_mut().state, - from.as_ptr(), - to.as_ptr(), - ) }; - io::result_from((),return_code) + let return_code = unsafe { + ll::lfs_rename( + &mut self.alloc.borrow_mut().state, + from.as_ptr(), + to.as_ptr(), + ) + }; + io::result_from((), return_code) } /// Given a path, query the filesystem to get information about a file or directory. @@ -372,7 +395,6 @@ impl Filesystem<'_, Storage> { /// To read user attributes, use /// [`Filesystem::attribute`](struct.Filesystem.html#method.attribute) pub fn metadata(&self, path: &Path) -> Result { - // do *not* not call assume_init here and pass into the unsafe block. // strange things happen ;) @@ -380,13 +402,8 @@ impl Filesystem<'_, Storage> { // I think it's fine, as we immediately copy out the data // to our own structure. let mut info: ll::lfs_info = unsafe { mem::MaybeUninit::zeroed().assume_init() }; - let return_code = unsafe { - ll::lfs_stat( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - &mut info, - ) - }; + let return_code = + unsafe { ll::lfs_stat(&mut self.alloc.borrow_mut().state, path.as_ptr(), &mut info) }; io::result_from((), return_code).map(|_| info.into()) } @@ -395,9 +412,7 @@ impl Filesystem<'_, Storage> { &self, path: &Path, f: impl FnOnce(&File<'_, '_, Storage>) -> Result, - ) -> - Result - { + ) -> Result { File::create_and_then(self, path, f) } @@ -405,9 +420,7 @@ impl Filesystem<'_, Storage> { &self, path: &Path, f: impl FnOnce(&File<'_, '_, Storage>) -> Result, - ) -> - Result - { + ) -> Result { File::open_and_then(self, path, f) } @@ -420,38 +433,32 @@ impl Filesystem<'_, Storage> { o: impl FnOnce(&mut OpenOptions) -> &OpenOptions, path: &Path, f: impl FnOnce(&File<'_, '_, Storage>) -> Result, - ) -> Result - { + ) -> Result { let mut options = OpenOptions::new(); o(&mut options).open_and_then(self, path, f) } - /// Read attribute. - pub fn attribute( - &self, - path: &Path, - id: u8, - ) -> - Result> - { + pub fn attribute(&self, path: &Path, id: u8) -> Result> { let mut attribute = Attribute::new(id); let attr_max = crate::consts::ATTRBYTES_MAX; - let return_code = unsafe { ll::lfs_getattr( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - id, - &mut attribute.data as *mut _ as *mut cty::c_void, - attr_max, - ) }; + let return_code = unsafe { + ll::lfs_getattr( + &mut self.alloc.borrow_mut().state, + path.as_ptr(), + id, + &mut attribute.data as *mut _ as *mut cty::c_void, + attr_max, + ) + }; if return_code >= 0 { attribute.size = cmp::min(attr_max, return_code as u32) as usize; return Ok(Some(attribute)); } if return_code == ll::lfs_error_LFS_ERR_NOATTR { - return Ok(None) + return Ok(None); } io::result_from((), return_code)?; @@ -460,39 +467,27 @@ impl Filesystem<'_, Storage> { } /// Remove attribute. - pub fn remove_attribute( - &self, - path: &Path, - id: u8, - ) -> Result<()> { - let return_code = unsafe { ll::lfs_removeattr( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - id, - ) }; + pub fn remove_attribute(&self, path: &Path, id: u8) -> Result<()> { + let return_code = + unsafe { ll::lfs_removeattr(&mut self.alloc.borrow_mut().state, path.as_ptr(), id) }; io::result_from((), return_code) } /// Set attribute. - pub fn set_attribute( - &self, - path: &Path, - attribute: &Attribute, - ) -> - Result<()> - { - let return_code = unsafe { ll::lfs_setattr( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - attribute.id, - &attribute.data as *const _ as *const cty::c_void, - attribute.size as u32, - ) }; + pub fn set_attribute(&self, path: &Path, attribute: &Attribute) -> Result<()> { + let return_code = unsafe { + ll::lfs_setattr( + &mut self.alloc.borrow_mut().state, + path.as_ptr(), + attribute.id, + &attribute.data as *const _ as *const cty::c_void, + attribute.size as u32, + ) + }; io::result_from((), return_code) } - /// C callback interface used by LittleFS to read data with the lower level system below the /// filesystem. extern "C" fn lfs_config_read( @@ -534,15 +529,12 @@ impl Filesystem<'_, Storage> { /// C callback interface used by LittleFS to erase data with the lower level system below the /// filesystem. - extern "C" fn lfs_config_erase( - c: *const ll::lfs_config, - block: ll::lfs_block_t, - ) -> cty::c_int { + extern "C" fn lfs_config_erase(c: *const ll::lfs_config, block: ll::lfs_block_t) -> cty::c_int { // println!("in lfs_config_erase"); let storage = unsafe { &mut *((*c).context as *mut Storage) }; - let off = block as usize * Storage::BLOCK_SIZE as usize; + let off = block as usize * Storage::BLOCK_SIZE; - io::error_code_from(storage.erase(off, Storage::BLOCK_SIZE as usize)) + io::error_code_from(storage.erase(off, Storage::BLOCK_SIZE)) } /// C callback interface used by LittleFS to sync data with the lower level interface below the @@ -552,10 +544,9 @@ impl Filesystem<'_, Storage> { // Do nothing; we presume that data is synchronized. 0 } - } -#[derive(Clone,Debug,Eq,PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] /// Custom user attribute that can be set on files and directories. /// /// Consists of an numerical identifier between 0 and 255, and arbitrary @@ -624,8 +615,7 @@ bitflags! { } /// The state of a `File`. Pre-allocate with `File::allocate`. -pub struct FileAllocation -{ +pub struct FileAllocation { cache: Bytes, state: ll::lfs_file_t, config: ll::lfs_file_config, @@ -645,14 +635,12 @@ impl FileAllocation { } } -pub struct File<'a, 'b, S: driver::Storage> -{ +pub struct File<'a, 'b, S: driver::Storage> { alloc: RefCell<&'b mut FileAllocation>, fs: &'b Filesystem<'a, S>, } -impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> -{ +impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> { pub fn allocate() -> FileAllocation { FileAllocation::new() } @@ -672,34 +660,24 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> pub unsafe fn open( fs: &'b Filesystem<'a, Storage>, alloc: &'b mut FileAllocation, - path: &Path, - ) -> - Result - { - OpenOptions::new() - .read(true) - .open(fs, alloc, path) + path: &Path, + ) -> Result { + OpenOptions::new().read(true).open(fs, alloc, path) } pub fn open_and_then( fs: &Filesystem<'a, Storage>, path: &Path, f: impl FnOnce(&File<'_, '_, Storage>) -> Result, - ) -> - Result - { - OpenOptions::new() - .read(true) - .open_and_then(fs, path, f) + ) -> Result { + OpenOptions::new().read(true).open_and_then(fs, path, f) } pub unsafe fn create( fs: &'b Filesystem<'a, Storage>, alloc: &'b mut FileAllocation, - path: &Path, - ) -> - Result - { + path: &Path, + ) -> Result { OpenOptions::new() .write(true) .create(true) @@ -711,9 +689,7 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> fs: &Filesystem<'a, Storage>, path: &Path, f: impl FnOnce(&File<'_, '_, Storage>) -> Result, - ) -> - Result - { + ) -> Result { OpenOptions::new() .write(true) .create(true) @@ -723,7 +699,7 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> // Safety-hatch to experiment with missing parts of API pub unsafe fn borrow_filesystem<'c>(&'c mut self) -> &'c Filesystem<'a, Storage> { - &self.fs + self.fs } /// Sync the file and drop it from the internal linked list. @@ -739,8 +715,7 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> /// - Since we don't have dynamically allocated buffers, at least we don't hit the double-free. /// - Not sure what happens in `lfs_file_sync`, but it should be easy to just error on /// not LFS_F_OPENED... - pub unsafe fn close(self) -> Result<()> - { + pub unsafe fn close(self) -> Result<()> { let return_code = ll::lfs_file_close( &mut self.fs.alloc.borrow_mut().state, &mut self.alloc.borrow_mut().state, @@ -750,33 +725,43 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> /// Synchronize file contents to storage. pub fn sync(&self) -> Result<()> { - let return_code = unsafe { ll::lfs_file_sync( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - ) }; + let return_code = unsafe { + ll::lfs_file_sync( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + ) + }; io::result_from((), return_code) } /// Size of the file in bytes. pub fn len(&self) -> Result { - let return_code = unsafe { ll::lfs_file_size( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state - ) }; + let return_code = unsafe { + ll::lfs_file_size( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + ) + }; io::result_from(return_code as usize, return_code) } + pub fn is_empty(&self) -> Result { + self.len().map(|l| l == 0) + } + /// Truncates or extends the underlying file, updating the size of this file to become size. /// /// If the size is less than the current file's size, then the file will be shrunk. If it is /// greater than the current file's size, then the file will be extended to size and have all /// of the intermediate data filled in with 0s. pub fn set_len(&self, size: usize) -> Result<()> { - let return_code = unsafe { ll::lfs_file_truncate( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - size as u32, - ) }; + let return_code = unsafe { + ll::lfs_file_truncate( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + size as u32, + ) + }; io::result_from((), return_code) } @@ -809,7 +794,6 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> } } - /// Options and flags which can be used to configure how a file is opened. /// /// This builder exposes the ability to configure how a File is opened and what operations @@ -817,8 +801,8 @@ impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> /// for commonly used options using this builder. /// /// Consider `File::with_options()` to avoid having to `use` OpenOptions. -#[derive(Clone,Debug,Eq,PartialEq)] -pub struct OpenOptions (FileOpenFlags); +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct OpenOptions(FileOpenFlags); impl Default for OpenOptions { fn default() -> Self { @@ -827,7 +811,6 @@ impl Default for OpenOptions { } impl OpenOptions { - /// Open the file with the options previously specified, keeping references. /// /// unsafe since UB can arise if files are not closed (see below). @@ -844,17 +827,15 @@ impl OpenOptions { fs: &'b Filesystem<'a, S>, alloc: &'b mut FileAllocation, path: &Path, - ) -> - Result> - { + ) -> Result> { alloc.config.buffer = &mut alloc.cache as *mut _ as *mut cty::c_void; let return_code = ll::lfs_file_opencfg( - &mut fs.alloc.borrow_mut().state, - &mut alloc.state, - path.as_ptr(), - self.0.bits() as i32, - &alloc.config, + &mut fs.alloc.borrow_mut().state, + &mut alloc.state, + path.as_ptr(), + self.0.bits() as i32, + &alloc.config, ); let file = File { @@ -871,9 +852,7 @@ impl OpenOptions { fs: &Filesystem<'a, S>, path: &Path, f: impl FnOnce(&File<'a, '_, S>) -> Result, - ) - -> Result - { + ) -> Result { let mut alloc = FileAllocation::new(); // lifetime 'c let mut file = unsafe { self.open(fs, &mut alloc, path)? }; // Q: what is the actually correct behaviour? @@ -894,7 +873,8 @@ impl OpenOptions { self.0.insert(FileOpenFlags::READ) } else { self.0.remove(FileOpenFlags::READ) - }; self + }; + self } pub fn write(&mut self, write: bool) -> &mut Self { @@ -902,7 +882,8 @@ impl OpenOptions { self.0.insert(FileOpenFlags::WRITE) } else { self.0.remove(FileOpenFlags::WRITE) - }; self + }; + self } pub fn append(&mut self, append: bool) -> &mut Self { @@ -910,7 +891,8 @@ impl OpenOptions { self.0.insert(FileOpenFlags::APPEND) } else { self.0.remove(FileOpenFlags::APPEND) - }; self + }; + self } pub fn create(&mut self, create: bool) -> &mut Self { @@ -918,7 +900,8 @@ impl OpenOptions { self.0.insert(FileOpenFlags::CREATE) } else { self.0.remove(FileOpenFlags::CREATE) - }; self + }; + self } pub fn create_new(&mut self, create_new: bool) -> &mut Self { @@ -928,7 +911,8 @@ impl OpenOptions { } else { self.0.remove(FileOpenFlags::EXCL); self.0.remove(FileOpenFlags::CREATE); - }; self + }; + self } pub fn truncate(&mut self, truncate: bool) -> &mut Self { @@ -936,53 +920,58 @@ impl OpenOptions { self.0.insert(FileOpenFlags::TRUNCATE) } else { self.0.remove(FileOpenFlags::TRUNCATE) - }; self + }; + self } - } -impl io::Read for File<'_, '_, S> -{ +impl io::Read for File<'_, '_, S> { fn read(&self, buf: &mut [u8]) -> Result { - let return_code = unsafe { ll::lfs_file_read( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - buf.as_mut_ptr() as *mut cty::c_void, - buf.len() as u32, - ) }; + let return_code = unsafe { + ll::lfs_file_read( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + buf.as_mut_ptr() as *mut cty::c_void, + buf.len() as u32, + ) + }; io::result_from(return_code as usize, return_code) } } -impl io::Seek for File<'_, '_, S> -{ +impl io::Seek for File<'_, '_, S> { fn seek(&self, pos: io::SeekFrom) -> Result { - let return_code = unsafe { ll::lfs_file_seek( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - pos.off(), - pos.whence(), - ) }; + let return_code = unsafe { + ll::lfs_file_seek( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + pos.off(), + pos.whence(), + ) + }; io::result_from(return_code as usize, return_code) } } -impl io::Write for File<'_, '_, S> -{ +impl io::Write for File<'_, '_, S> { fn write(&self, buf: &[u8]) -> Result { - let return_code = unsafe { ll::lfs_file_write( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - buf.as_ptr() as *const cty::c_void, - buf.len() as u32, - ) }; + let return_code = unsafe { + ll::lfs_file_write( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + buf.as_ptr() as *const cty::c_void, + buf.len() as u32, + ) + }; io::result_from(return_code as usize, return_code) } - fn flush(&self) -> Result<()> { Ok(()) } + fn flush(&self) -> Result<()> { + Ok(()) + } } -#[derive(Clone,Debug,PartialEq,Eq,Serialize,Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DirEntry { file_name: PathBuf, metadata: Metadata, @@ -1024,7 +1013,6 @@ impl DirEntry { pub unsafe fn path_buf_mut(&mut self) -> &mut PathBuf { &mut self.path } - } pub struct ReadDirAllocation { @@ -1043,24 +1031,20 @@ impl ReadDirAllocation { } } -pub struct ReadDir<'a, 'b, S: driver::Storage> -{ +pub struct ReadDir<'a, 'b, S: driver::Storage> { alloc: RefCell<&'b mut ReadDirAllocation>, fs: &'b Filesystem<'a, S>, #[cfg(feature = "dir-entry-path")] path: PathBuf, } -impl<'a, 'b, S: driver::Storage> Iterator for ReadDir<'a, 'b, S> -{ +impl<'a, 'b, S: driver::Storage> Iterator for ReadDir<'a, 'b, S> { type Item = Result; // remove this allowance again, once path overflow is properly handled #[allow(unreachable_code)] fn next(&mut self) -> Option { - let mut info: ll::lfs_info = unsafe { - mem::MaybeUninit::zeroed().assume_init() - }; + let mut info: ll::lfs_info = unsafe { mem::MaybeUninit::zeroed().assume_init() }; let return_code = unsafe { ll::lfs_dir_read( @@ -1088,7 +1072,7 @@ impl<'a, 'b, S: driver::Storage> Iterator for ReadDir<'a, 'b, S> } if return_code == 0 { - return None + return None; } Some(Err(io::result_from((), return_code).unwrap_err())) @@ -1096,10 +1080,9 @@ impl<'a, 'b, S: driver::Storage> Iterator for ReadDir<'a, 'b, S> } impl<'a, 'b, S: driver::Storage> ReadDir<'a, 'b, S> { - // Safety-hatch to experiment with missing parts of API pub unsafe fn borrow_filesystem<'c>(&'c mut self) -> &'c Filesystem<'a, S> { - &self.fs + self.fs } } @@ -1111,26 +1094,24 @@ impl ReadDir<'_, '_, S> { // Although I guess if the compiler reuses the ReadDirAllocation, and we still // have an (unsafely genereated) ReadDir with that handle; on the other hand // as long as ReadDir is not Copy. - pub /* unsafe */ fn close(self) -> Result<()> - { - let return_code = unsafe { ll::lfs_dir_close( - &mut self.fs.alloc.borrow_mut().state, - &mut self.alloc.borrow_mut().state, - ) }; + pub fn close(self) -> Result<()> { + let return_code = unsafe { + ll::lfs_dir_close( + &mut self.fs.alloc.borrow_mut().state, + &mut self.alloc.borrow_mut().state, + ) + }; io::result_from((), return_code) } } - impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { - pub fn read_dir_and_then( &self, path: &Path, // *not* &ReadDir, as Iterator takes &mut f: impl FnOnce(&mut ReadDir<'_, '_, Storage>) -> Result, - ) -> Result - { + ) -> Result { let mut alloc = ReadDirAllocation::new(); let mut read_dir = unsafe { self.read_dir(&mut alloc, path)? }; let res = f(&mut read_dir); @@ -1139,16 +1120,14 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { res } - /// Returns a pseudo-iterator over the entries within a directory. + /// Returns a pseudo-iterator over the entries within a directory. /// /// This is unsafe since it can induce UB just like File::open. - pub unsafe fn read_dir<'b>( + pub unsafe fn read_dir<'b>( &'b self, alloc: &'b mut ReadDirAllocation, path: &Path, - ) -> - Result> - { + ) -> Result> { let return_code = ll::lfs_dir_open( &mut self.alloc.borrow_mut().state, &mut alloc.state, @@ -1164,16 +1143,10 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { io::result_from(read_dir, return_code) } - } - impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { - - pub fn mount( - alloc: &'a mut Allocation, - storage: &'a mut Storage, - ) -> Result { + pub fn mount(alloc: &'a mut Allocation, storage: &'a mut Storage) -> Result { let fs = Self::new(alloc, storage); let mut alloc = fs.alloc.borrow_mut(); let return_code = unsafe { ll::lfs_mount(&mut alloc.state, &alloc.config) }; @@ -1183,14 +1156,16 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { // Not public, user should use `mount`, possibly after `format` fn new(alloc: &'a mut Allocation, storage: &'a mut Storage) -> Self { - alloc.config.context = storage as *mut _ as *mut cty::c_void; alloc.config.read_buffer = &mut alloc.cache.read as *mut _ as *mut cty::c_void; alloc.config.prog_buffer = &mut alloc.cache.write as *mut _ as *mut cty::c_void; alloc.config.lookahead_buffer = &mut alloc.cache.lookahead as *mut _ as *mut cty::c_void; - Filesystem { alloc: RefCell::new(alloc), storage } + Filesystem { + alloc: RefCell::new(alloc), + storage, + } } /// Deconstruct `Filesystem`, intention is to allow access to @@ -1203,13 +1178,10 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { /// Creates a new, empty directory at the provided path. pub fn create_dir(&self, path: &Path) -> Result<()> { - #[cfg(test)] println!("creating {:?}", path); - let return_code = unsafe { ll::lfs_mkdir( - &mut self.alloc.borrow_mut().state, - path.as_ptr(), - ) }; + let return_code = + unsafe { ll::lfs_mkdir(&mut self.alloc.borrow_mut().state, path.as_ptr()) }; io::result_from((), return_code) } @@ -1229,14 +1201,18 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { match self.create_dir(&dir) { Ok(_) => {} Err(io::Error::EntryAlreadyExisted) => {} - error => { panic!("{:?}", &error); } + error => { + panic!("{:?}", &error); + } } } } match self.create_dir(path) { Ok(_) => {} Err(io::Error::EntryAlreadyExisted) => {} - error => { panic!("{:?}", &error); } + error => { + panic!("{:?}", &error); + } } Ok(()) @@ -1259,17 +1235,11 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { // Ok(()) => return Ok(()), // Err(e) => return Err(e), // } - } /// Read the entire contents of a file into a bytes vector. - pub fn read( - &self, - path: &Path, - ) - -> Result> - { - let mut contents: heapless::Vec:: = Default::default(); + pub fn read(&self, path: &Path) -> Result> { + let mut contents: heapless::Vec = Default::default(); File::open_and_then(self, path, |file| { // use io::Read; let len = file.read_to_end(&mut contents)?; @@ -1282,12 +1252,7 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { /// /// This function will create a file if it does not exist, /// and will entirely replace its contents if it does. - pub fn write( - &self, - path: &Path, - contents: &[u8], - ) -> Result<()> - { + pub fn write(&self, path: &Path, contents: &[u8]) -> Result<()> { #[cfg(test)] println!("writing {:?}", path); File::create_and_then(self, path, |file| { @@ -1296,7 +1261,6 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { })?; Ok(()) } - } #[cfg(test)] @@ -1316,7 +1280,6 @@ mod tests { Filesystem::format(&mut test_storage).unwrap(); Filesystem::mount_and_then(&mut test_storage, |fs| { - println!("blocks going in: {}", fs.available_blocks()?); fs.create_dir_all(b"/tmp/test\0".try_into().unwrap())?; println!("dir done"); @@ -1355,7 +1318,8 @@ mod tests { for entry in read_dir { let entry = entry?; println!("entry: {:?}", entry.file_name()); - #[cfg(feature = "dir-entry-path")] { + #[cfg(feature = "dir-entry-path")] + { println!("path: {:?}", entry.path()); let mut attribute = Attribute::new(37); @@ -1379,14 +1343,17 @@ mod tests { println!("\nfile {}: {:?}", i, entry.file_name()); if entry.file_type().is_file() { - let content: heapless::Vec:: = fs.read(entry.path())?; + let content: heapless::Vec = fs.read(entry.path())?; println!("content:\n{:?}", core::str::from_utf8(&content).unwrap()); // println!("and now the removal"); // fs.remove(entry.path())?; } if let Some(attribute) = fs.attribute(entry.path(), 37)? { - println!("attribute 37: {:?}", core::str::from_utf8(attribute.data()).unwrap()); + println!( + "attribute 37: {:?}", + core::str::from_utf8(attribute.data()).unwrap() + ); } // deleting (self) file while iterating! @@ -1407,12 +1374,12 @@ mod tests { println!("writing new file"); fs.write(b"/tmp/test/out-of-nowhere.txt\0".try_into().unwrap(), &[])?; } - } Ok(()) })?; - #[cfg(feature = "dir-entry-path")] { + #[cfg(feature = "dir-entry-path")] + { println!("\nDELETION SPREE\n"); // behaves veeryweirldy // (...) @@ -1424,12 +1391,13 @@ mod tests { } Ok(()) - }).unwrap(); + }) + .unwrap(); let mut alloc = Allocation::new(); let fs = Filesystem::mount(&mut alloc, &mut test_storage).unwrap(); // fs.write(b"/z.txt\0".try_into().unwrap(), &jackson5).unwrap(); - fs.write(&PathBuf::from("z.txt"), &jackson5).unwrap(); + fs.write(&PathBuf::from("z.txt"), jackson5).unwrap(); } #[cfg(feature = "dir-entry-path")] @@ -1441,7 +1409,6 @@ mod tests { Filesystem::format(&mut test_storage).unwrap(); Filesystem::mount_and_then(&mut test_storage, |fs| { - fs.create_dir_all(b"/tmp/test\0".try_into().unwrap())?; fs.write(b"/tmp/test/a.txt\0".try_into().unwrap(), jackson5)?; fs.write(b"/tmp/test/b.txt\0".try_into().unwrap(), jackson5)?; @@ -1451,7 +1418,8 @@ mod tests { fs.remove_dir_all(b"/tmp\0".try_into().unwrap())?; Ok(()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -1477,14 +1445,15 @@ mod tests { file.write(b"second part")?; Ok(()) - } + }, )?; let content: heapless::Vec<_, 256> = fs.read(filename)?; assert_eq!(content, b"first part - second part"); // println!("content: {:?}", core::str::from_utf8(&content).unwrap()); Ok(()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -1493,7 +1462,6 @@ mod tests { Filesystem::format(&mut test_storage).unwrap(); Filesystem::mount_and_then(&mut test_storage, |fs| { - fs.write(b"a.txt\0".try_into().unwrap(), &[])?; fs.write(b"b.txt\0".try_into().unwrap(), &[])?; fs.write(b"c.txt\0".try_into().unwrap(), &[])?; @@ -1508,27 +1476,23 @@ mod tests { // One usecase is to read data from the files iterated over. // if entry.metadata.is_file() { - fs.write( - &entry.file_name(), - b"wowee zowie" - )?; + fs.write(entry.file_name(), b"wowee zowie")?; } } Ok(()) })?; Ok(()) - }).unwrap(); + }) + .unwrap(); } - #[test] fn issue_3_original_report() { let mut test_storage = TestStorage::new(); Filesystem::format(&mut test_storage).unwrap(); Filesystem::mount_and_then(&mut test_storage, |fs| { - fs.write(b"a.txt\0".try_into().unwrap(), &[])?; fs.write(b"b.txt\0".try_into().unwrap(), &[])?; fs.write(b"c.txt\0".try_into().unwrap(), &[])?; @@ -1542,19 +1506,19 @@ mod tests { Ok(()) })?; - let mut a1 = File::allocate(); - let f1 = unsafe { File::open(&fs, &mut a1, b"a.txt\0".try_into().unwrap())? }; + let f1 = unsafe { File::open(fs, &mut a1, b"a.txt\0".try_into().unwrap())? }; f1.write(b"some text")?; let mut a2 = File::allocate(); - let f2 = unsafe { File::open(&fs, &mut a2, b"b.txt\0".try_into().unwrap())? }; + let f2 = unsafe { File::open(fs, &mut a2, b"b.txt\0".try_into().unwrap())? }; f2.write(b"more text")?; unsafe { f1.close()? }; // program hangs here unsafe { f2.close()? }; // this statement is never reached Ok(()) - }).unwrap(); + }) + .unwrap(); } } diff --git a/src/io.rs b/src/io.rs index 64d4cc7b0..9d18ce80f 100644 --- a/src/io.rs +++ b/src/io.rs @@ -20,7 +20,6 @@ pub trait Read { Err(Error::Io) } } - } /** The `Write` trait allows for writing bytes to a file. @@ -44,7 +43,7 @@ pub trait Write { match self.write(buf) { Ok(0) => { // failed to write whole buffer - return Err(Error::Io) + return Err(Error::Io); } Ok(n) => buf = &buf[n..], Err(e) => return Err(e), @@ -58,7 +57,7 @@ pub trait Write { Use the [`Seek`](../io/trait.Seek.html) trait. */ -#[derive(Clone,Copy,Debug,Eq,PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SeekFrom { Start(u32), End(i32), @@ -96,7 +95,7 @@ pub trait Seek { pub type Result = core::result::Result; /// Definition of errors that might be returned by filesystem functionality. -#[derive(Clone,Copy,Debug,PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Error { /// Error code was >=0, operation was successful. Success, @@ -194,6 +193,6 @@ pub fn result_from(return_value: T, error_code: ll::lfs_error) -> Result { let error: Error = error_code.into(); match error { Error::Success => Ok(return_value), - _ => Err(error) + _ => Err(error), } } diff --git a/src/io/prelude.rs b/src/io/prelude.rs index cdd4d0b5d..e0100b200 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -1,8 +1,3 @@ //! Export of the Read, Write and Seek traits for ease of use. -pub use super::{ - Read, - Write, - Seek, - SeekFrom, -}; +pub use super::{Read, Seek, SeekFrom, Write}; diff --git a/src/lib.rs b/src/lib.rs index 4ecd1d6a3..e11185f7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ #![cfg_attr(not(test), no_std)] +// FIXME +#![allow(clippy::missing_safety_doc)] /*! @@ -153,11 +155,11 @@ pub fn version() -> Version { } /// Information about the C backend -#[derive(Clone,Copy,Debug)] +#[derive(Clone, Copy, Debug)] pub struct Version { - /// On-disk format (currently: 2.0) + /// On-disk format (currently: 2.0) pub format: (u32, u32), - /// Backend release (currently: 2.1) + /// Backend release (currently: 2.1) pub backend: (u32, u32), } diff --git a/src/path.rs b/src/path.rs index c31c6216f..cf873d1e1 100644 --- a/src/path.rs +++ b/src/path.rs @@ -15,6 +15,7 @@ use crate::consts; /// accepts arbitrary C strings), but the assumption makes `AsRef` trivial /// to implement. #[derive(PartialEq, Eq)] +#[repr(transparent)] pub struct Path { inner: CStr, } @@ -34,10 +35,7 @@ impl Path { } assert!(!bytes.is_empty(), "must not be empty"); assert!(bytes[i] == 0, "last byte must be null"); - unsafe { - Self::from_bytes_with_nul_unchecked(bytes) - } - + unsafe { Self::from_bytes_with_nul_unchecked(bytes) } } /// Creates a path from a byte buffer @@ -182,8 +180,8 @@ macro_rules! array_impls { } array_impls!( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32 ); /// An owned, mutable path @@ -205,9 +203,18 @@ unsafe fn strlen(mut s: *const c_char) -> size_t { n } +impl Default for PathBuf { + fn default() -> Self { + Self::new() + } +} + impl PathBuf { pub fn new() -> Self { - Self { buf: [0; consts::PATH_MAX_PLUS_ONE], len: 1 } + Self { + buf: [0; consts::PATH_MAX_PLUS_ONE], + len: 1, + } } pub fn clear(&mut self) { @@ -284,10 +291,7 @@ impl From<&Path> for PathBuf { let len = bytes.len(); assert!(len <= consts::PATH_MAX); unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr().cast(), len + 1) } - Self { - buf, - len: len + 1, - } + Self { buf, len: len + 1 } } } @@ -303,7 +307,7 @@ impl From<&[u8]> for PathBuf { } else { bytes }; - let has_no_embedded_nul = bytes.iter().find(|&&byte| byte == b'\0').is_none(); + let has_no_embedded_nul = !bytes.iter().any(|&byte| byte == b'\0'); assert!(has_no_embedded_nul); let mut buf = [0; consts::PATH_MAX_PLUS_ONE]; @@ -311,10 +315,7 @@ impl From<&[u8]> for PathBuf { assert!(len <= consts::PATH_MAX); assert!(bytes.is_ascii()); unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr().cast(), len) } - Self { - buf, - len: len + 1, - } + Self { buf, len: len + 1 } } } @@ -337,7 +338,6 @@ impl ops::Deref for PathBuf { } } - impl serde::Serialize for PathBuf { fn serialize(&self, serializer: S) -> core::result::Result where @@ -347,16 +347,14 @@ impl serde::Serialize for PathBuf { } } -impl<'de> serde::Deserialize<'de> for PathBuf -{ +impl<'de> serde::Deserialize<'de> for PathBuf { fn deserialize(deserializer: D) -> core::result::Result where D: serde::Deserializer<'de>, { struct ValueVisitor<'de>(PhantomData<&'de ()>); - impl<'de> serde::de::Visitor<'de> for ValueVisitor<'de> - { + impl<'de> serde::de::Visitor<'de> for ValueVisitor<'de> { type Value = PathBuf; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -435,8 +433,8 @@ pub type Result = core::result::Result; #[cfg(test)] mod tests { - use crate::path; use super::{Path, PathBuf}; + use crate::path; const EMPTY: &Path = path!(""); const SLASH: &Path = path!("/"); diff --git a/src/tests.rs b/src/tests.rs index fb99c34b3..a28f2b260 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -2,18 +2,10 @@ use core::convert::TryInto; use generic_array::typenum::consts; use crate::{ - fs::{ - Attribute, - File, - Filesystem, - }, - io::{ - Error, - Result, - Read, - SeekFrom, - }, driver, + fs::{Attribute, File, Filesystem}, + io::{Error, Read, Result, SeekFrom}, + path::Path, }; ram_storage!( @@ -63,7 +55,8 @@ fn format() { // should fail: FS is not formatted assert_eq!( Filesystem::mount(&mut alloc, &mut storage) - .map(drop).unwrap_err(), + .map(drop) + .unwrap_err(), Error::Corruption ); // should succeed @@ -97,8 +90,10 @@ fn borrow_fs_allocation() { // previous `_fs` is fine as it's masked, due to NLL let fs = Filesystem::mount(&mut alloc_fs, &mut storage).unwrap(); - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); } #[test] @@ -112,11 +107,14 @@ fn borrow_fs_allocation2() { // previous `_fs` is fine as it's masked, due to NLL Filesystem::mount_and_then(&mut storage, |fs| { - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); // where is boats when you need him lol Ok(()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -125,16 +123,17 @@ fn borrow_fs_allocation3() { let mut storage = OtherRamStorage::new(&mut backend); Filesystem::format(&mut storage).unwrap(); - Filesystem::mount_and_then(&mut storage, |_| { - Ok(()) - }).unwrap(); + Filesystem::mount_and_then(&mut storage, |_| Ok(())).unwrap(); Filesystem::mount_and_then(&mut storage, |fs| { - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); - fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| { Ok(()) }).unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); + fs.create_file_and_then(b"data.bin\0".try_into().unwrap(), |_| Ok(())) + .unwrap(); // where is boats when you need him lol Ok(()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -144,25 +143,31 @@ fn test_fs_with() -> Result<()> { Filesystem::format(&mut storage).unwrap(); Filesystem::mount_and_then(&mut storage, |fs| { - assert_eq!(fs.total_blocks(), 512); - assert_eq!(fs.total_space(), 256*512); + assert_eq!(fs.total_space(), 256 * 512); // superblock assert_eq!(fs.available_blocks()?, 512 - 2); assert_eq!(fs.available_space()?, 130_560); - fs.create_dir(b"/tmp\0".try_into().unwrap())?; assert_eq!(fs.available_blocks()?, 512 - 4); - fs.create_dir(b"/mnt\0".try_into().unwrap())?; assert_eq!(fs.available_blocks()?, 512 - 6); + fs.create_dir(b"/tmp\0".try_into().unwrap())?; + assert_eq!(fs.available_blocks()?, 512 - 4); + fs.create_dir(b"/mnt\0".try_into().unwrap())?; + assert_eq!(fs.available_blocks()?, 512 - 6); fs.rename( b"tmp\0".try_into().unwrap(), - b"mnt/tmp\0".try_into().unwrap())?; assert_eq!(fs.available_blocks()?, 512 - 6); - fs.remove(b"/mnt/tmp\0".try_into().unwrap())?; assert_eq!(fs.available_blocks()?, 512 - 4); - fs.remove(b"/mnt\0".try_into().unwrap())?; assert_eq!(fs.available_blocks()?, 512 - 2); + b"mnt/tmp\0".try_into().unwrap(), + )?; + assert_eq!(fs.available_blocks()?, 512 - 6); + fs.remove(b"/mnt/tmp\0".try_into().unwrap())?; + assert_eq!(fs.available_blocks()?, 512 - 4); + fs.remove(b"/mnt\0".try_into().unwrap())?; + assert_eq!(fs.available_blocks()?, 512 - 2); fs.create_file_and_then(b"/test_with.txt\0".try_into().unwrap(), |file| { assert!(file.write(&[0u8, 1, 2])? == 3); Ok(()) - }).unwrap(); + }) + .unwrap(); let mut buf = [0u8; 3]; fs.open_file_and_then(b"/test_with.txt\0".try_into().unwrap(), |file| { @@ -170,7 +175,8 @@ fn test_fs_with() -> Result<()> { assert!(file.read_exact(&mut buf).is_ok()); assert_eq!(&buf, &[0, 1, 2]); Ok(()) - }).unwrap(); + }) + .unwrap(); // surprise surprise, inline files! assert_eq!(fs.available_blocks()?, 512 - 2); @@ -187,27 +193,26 @@ fn test_create() { Filesystem::format(&mut storage).unwrap(); Filesystem::mount_and_then(&mut storage, |fs| { assert_eq!(fs.total_blocks(), 512); - assert_eq!(fs.total_space(), 256*512); + assert_eq!(fs.total_space(), 256 * 512); // superblock assert_eq!(fs.available_blocks().unwrap(), 512 - 2); assert_eq!(fs.available_space().unwrap(), 130_560); - assert!(!crate::path::PathBuf::from(b"/test_open.txt").exists(&fs)); + assert!(!crate::path::PathBuf::from(b"/test_open.txt").exists(fs)); assert_eq!( File::open_and_then(fs, b"/test_open.txt\0".try_into().unwrap(), |_| { Ok(()) }) .map(drop) .unwrap_err(), // "real" contains_err is experimental Error::NoSuchEntry ); - assert!(!crate::path::PathBuf::from(b"/test_open.txt").exists(&fs)); + assert!(!crate::path::PathBuf::from(b"/test_open.txt").exists(fs)); fs.create_dir(b"/tmp\0".try_into().unwrap()).unwrap(); assert_eq!(fs.available_blocks().unwrap(), 512 - 2 - 2); // can create new files - assert!(!crate::path::PathBuf::from(b"/tmp/test_open.txt").exists(&fs)); + assert!(!crate::path::PathBuf::from(b"/tmp/test_open.txt").exists(fs)); fs.create_file_and_then(b"/tmp/test_open.txt\0".try_into().unwrap(), |file| { - // can write to files assert!(file.write(&[0u8, 1, 2]).unwrap() == 3); file.sync()?; @@ -217,17 +222,23 @@ fn test_create() { // file.close()?; Ok(()) })?; - assert!(crate::path::PathBuf::from(b"/tmp/test_open.txt").exists(&fs)); + assert!(crate::path::PathBuf::from(b"/tmp/test_open.txt").exists(fs)); // // cannot remove non-empty directories - assert_eq!(fs.remove(b"/tmp\0".try_into().unwrap()).unwrap_err(), Error::DirNotEmpty); + assert_eq!( + fs.remove(b"/tmp\0".try_into().unwrap()).unwrap_err(), + Error::DirNotEmpty + ); let metadata = fs.metadata(b"/tmp\0".try_into().unwrap())?; assert!(metadata.is_dir()); assert_eq!(metadata.len(), 0); // can move files - fs.rename(b"/tmp/test_open.txt\0".try_into().unwrap(), b"moved.txt\0".try_into().unwrap())?; + fs.rename( + b"/tmp/test_open.txt\0".try_into().unwrap(), + b"moved.txt\0".try_into().unwrap(), + )?; assert_eq!(fs.available_blocks().unwrap(), 512 - 2 - 2); let metadata = fs.metadata(b"/moved.txt\0".try_into().unwrap())?; @@ -251,8 +262,8 @@ fn test_create() { })?; Ok(()) - - }).unwrap(); + }) + .unwrap(); } #[test] @@ -268,7 +279,8 @@ fn test_unbind() { assert_eq!(file.len()?, 11); Ok(()) }) - }).unwrap(); + }) + .unwrap(); } let mut storage = RamStorage::new(&mut backend); @@ -276,7 +288,8 @@ fn test_unbind() { let contents: heapless::Vec<_, 37> = fs.read(b"test_unbind.txt\0".try_into().unwrap())?; assert_eq!(contents, b"hello world"); Ok(()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -295,7 +308,8 @@ fn test_seek() { assert_eq!(&buf, b"world"); Ok(()) }) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -318,7 +332,8 @@ fn test_file_set_len() { assert_eq!(file.seek(SeekFrom::Current(0))?, 14); Ok(()) }) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -340,20 +355,49 @@ fn test_fancy_open() { file.seek(SeekFrom::Start(6))?; file.read(&mut buf) - } + }, ) - }).unwrap(); + }) + .unwrap(); assert_eq!(&buf, b"world"); } #[test] -fn attributes() { +fn remove_dir_all_where() { let mut backend = Ram::default(); let mut storage = RamStorage::new(&mut backend); + Filesystem::format(&mut storage).unwrap(); + Filesystem::mount_and_then(&mut storage, |fs| { + fs.write(path!("test_file"), b"some data").unwrap(); + fs.create_dir(path!("test_dir")).unwrap(); + fs.write(path!("test_dir/test_file"), b"some_inner_data") + .unwrap(); + fs.write(path!("test_dir/test_file2"), b"some_inner_data") + .unwrap(); + fs.remove_dir_all_where(path!(""), &|entry| { + entry.path() != path!("test_dir/test_file") + }) + .unwrap(); + assert!(fs.metadata(path!("test_dir/test_file")).unwrap().is_file()); + assert_eq!(fs.metadata(path!("test_file")), Err(Error::NoSuchEntry)); + assert_eq!( + fs.metadata(path!("test_dir/test_file2")), + Err(Error::NoSuchEntry) + ); + Ok(()) + }) + .unwrap(); +} +#[test] +fn attributes() { + let mut backend = Ram::default(); + let mut storage = RamStorage::new(&mut backend); + Filesystem::format(&mut storage).unwrap(); + Filesystem::mount_and_then(&mut storage, |fs| { let filename = b"some.file\0".try_into().unwrap(); fs.write(filename, &[])?; assert!(fs.attribute(filename, 37)?.is_none()); @@ -362,10 +406,7 @@ fn attributes() { attribute.set_data(b"top secret"); fs.set_attribute(filename, &attribute).unwrap(); - assert_eq!( - b"top secret", - fs.attribute(filename, 37)?.unwrap().data() - ); + assert_eq!(b"top secret", fs.attribute(filename, 37)?.unwrap().data()); // // not sure if we should have this method (may be quite expensive) // let attributes = unsafe { fs.attributes("some.file", &mut storage).unwrap() }; @@ -391,8 +432,8 @@ fn attributes() { assert!(fs.attribute(tmp_dir, 37)?.is_none()); Ok(()) - - }).unwrap(); + }) + .unwrap(); } #[test] @@ -401,15 +442,12 @@ fn test_iter_dirs() { let mut storage = RamStorage::new(&mut backend); Filesystem::format(&mut storage).unwrap(); Filesystem::mount_and_then(&mut storage, |fs| { - fs.create_dir(b"/tmp\0".try_into()?)?; // TODO: we might want "multi-open" fs.create_file_and_then(b"/tmp/file.a\0".try_into()?, |file| { file.set_len(37)?; - fs.create_file_and_then(b"/tmp/file.b\0".try_into()?, |file| { - file.set_len(42) - }) + fs.create_file_and_then(b"/tmp/file.b\0".try_into()?, |file| file.set_len(42)) })?; fs.read_dir_and_then(b"/tmp\0".try_into()?, |dir| { @@ -436,7 +474,8 @@ fn test_iter_dirs() { Ok(()) }) - }).unwrap(); + }) + .unwrap(); } // // These are some tests that ensure our type constructions diff --git a/tests/test_serde.rs b/tests/test_serde.rs index 21b11200e..63a46a4e7 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -1,17 +1,11 @@ -use littlefs2::{ - driver, - fs::Filesystem, - io::Result, - path::Path, - ram_storage, -}; - -use serde::{Deserialize,Serialize}; +use littlefs2::{driver, fs::Filesystem, io::Result, path::Path, ram_storage}; + +use serde::{Deserialize, Serialize}; use ssmarshal::{deserialize, serialize}; -#[derive(Serialize, Deserialize, PartialEq, Debug, Default/*, Desse, DesseSized*/)] +#[derive(Serialize, Deserialize, PartialEq, Debug, Default /*, Desse, DesseSized*/)] struct Entity { - z: [u8; 16], + z: [u8; 16], // x: u32, x: f32, y: u64, @@ -35,10 +29,16 @@ fn main() { let size = serialize(&mut buf, &entity).unwrap(); assert_eq!(size, 28); - fs.write(Path::from_bytes_with_nul(b"entity.bin\0").unwrap(), &buf[..size]).unwrap(); - fs.open_file_and_then(Path::from_bytes_with_nul(b"entity.bin\0").unwrap(), |file| { - file.read(&mut buf) - }).unwrap(); + fs.write( + Path::from_bytes_with_nul(b"entity.bin\0").unwrap(), + &buf[..size], + ) + .unwrap(); + fs.open_file_and_then( + Path::from_bytes_with_nul(b"entity.bin\0").unwrap(), + |file| file.read(&mut buf), + ) + .unwrap(); // let mut alloc = File::allocate(); // let mut file = File::create("entity.bin", &mut alloc, &mut fs, &mut storage).unwrap(); // file.write(&mut fs, &mut storage, &buf[..size]).unwrap();