From 1f8f8365d66eb30fef405f0988d0e087638728d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= Date: Thu, 28 Jun 2018 18:43:02 +0200 Subject: [PATCH] Add TimeProvider trait and time_provider option This functionality is very useful for embedded usage where chrono feature cannot be enabled. --- TODO.md | 1 - src/dir.rs | 7 ++++--- src/dir_entry.rs | 54 +++--------------------------------------------- src/file.rs | 15 ++++++++------ src/fs.rs | 44 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 62 deletions(-) diff --git a/TODO.md b/TODO.md index 7548ceb..13c3a00 100644 --- a/TODO.md +++ b/TODO.md @@ -4,5 +4,4 @@ TODO * support for a volume label file in the root directory * format volume API * add method for getting `DirEntry` from a path (possible names: metadata, lookup) -* add time provier so the crate writes a proper timestamps when `chrono` is unavailable * do not create LFN entries if the name fits in a SFN entry diff --git a/src/dir.rs b/src/dir.rs index 9c3a2bd..25e518a 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -406,9 +406,10 @@ impl<'a, T: ReadWriteSeek + 'a> Dir<'a, T> { fn create_sfn_entry(&self, short_name: [u8; 11], attrs: FileAttributes, first_cluster: Option) -> DirFileEntryData { let mut raw_entry = DirFileEntryData::new(short_name, attrs); raw_entry.set_first_cluster(first_cluster, self.fs.fat_type()); - raw_entry.reset_created(); - raw_entry.reset_accessed(); - raw_entry.reset_modified(); + let now = self.fs.options.time_provider.get_current_date_time(); + raw_entry.set_created(now); + raw_entry.set_accessed(now.date); + raw_entry.set_modified(now); raw_entry } diff --git a/src/dir_entry.rs b/src/dir_entry.rs index 3c60d88..1cc9727 100644 --- a/src/dir_entry.rs +++ b/src/dir_entry.rs @@ -219,61 +219,22 @@ impl DirFileEntryData { DateTime::decode(self.modify_date, self.modify_time, 0) } - fn set_created(&mut self, date_time: DateTime) { + pub(crate) fn set_created(&mut self, date_time: DateTime) { self.create_date = date_time.date.encode(); let encoded_time = date_time.time.encode(); self.create_time_1 = encoded_time.0; self.create_time_0 = encoded_time.1; } - fn set_accessed(&mut self, date: Date) { + pub(crate) fn set_accessed(&mut self, date: Date) { self.access_date = date.encode(); } - fn set_modified(&mut self, date_time: DateTime) { + pub(crate) fn set_modified(&mut self, date_time: DateTime) { self.modify_date = date_time.date.encode(); self.modify_time = date_time.time.encode().0; } - #[cfg(feature = "chrono")] - pub(crate) fn reset_created(&mut self) { - let now = DateTime::from(chrono::Local::now()); - self.set_created(now); - } - - #[cfg(feature = "chrono")] - pub(crate) fn reset_accessed(&mut self) -> bool { - let now = Date::from(chrono::Local::now().date()); - if now == self.accessed() { - false - } else { - self.set_accessed(now); - true - } - } - - #[cfg(feature = "chrono")] - pub(crate) fn reset_modified(&mut self) { - let now = DateTime::from(chrono::Local::now()); - self.set_modified(now); - } - - #[cfg(not(feature = "chrono"))] - pub(crate) fn reset_created(&mut self) { - // nop - user controls timestamps manually - } - - #[cfg(not(feature = "chrono"))] - pub(crate) fn reset_accessed(&mut self) -> bool { - // nop - user controls timestamps manually - false - } - - #[cfg(not(feature = "chrono"))] - pub(crate) fn reset_modified(&mut self) { - // nop - user controls timestamps manually - } - pub(crate) fn serialize(&self, wrt: &mut Write) -> io::Result<()> { wrt.write_all(&self.name)?; wrt.write_u8(self.attrs.bits())?; @@ -642,15 +603,6 @@ impl DirEntryEditor { } } - pub(crate) fn reset_accessed(&mut self) { - self.dirty |= self.data.reset_accessed(); - } - - pub(crate) fn reset_modified(&mut self) { - self.data.reset_modified(); - self.dirty = true; - } - pub(crate) fn flush(&mut self, fs: &FileSystem) -> io::Result<()> { if self.dirty { self.write(fs)?; diff --git a/src/file.rs b/src/file.rs index 04dea69..6732b35 100644 --- a/src/file.rs +++ b/src/file.rs @@ -36,10 +36,11 @@ impl<'a, T: ReadWriteSeek> File<'a, T> { } } - fn update_size(&mut self) { + fn update_dir_entry_after_write(&mut self) { let offset = self.offset; if let Some(ref mut e) = self.entry { - e.reset_modified(); + let now = self.fs.options.time_provider.get_current_date_time(); + e.set_modified(now); if e.inner().size().map_or(false, |s| offset > s) { e.set_size(offset); } @@ -199,9 +200,11 @@ impl<'a, T: ReadWriteSeek> Read for File<'a, T> { self.offset += read_bytes as u32; self.current_cluster = Some(current_cluster); - match self.entry { - Some(ref mut e) if self.fs.options.update_accessed_date => e.reset_accessed(), - _ => {}, + if let Some(ref mut e) = self.entry { + if self.fs.options.update_accessed_date { + let now = self.fs.options.time_provider.get_current_date(); + e.set_accessed(now); + } } Ok(read_bytes) } @@ -276,7 +279,7 @@ impl<'a, T: ReadWriteSeek> Write for File<'a, T> { // some bytes were writter - update position and optionally size self.offset += written_bytes as u32; self.current_cluster = Some(current_cluster); - self.update_size(); + self.update_dir_entry_after_write(); Ok(written_bytes) } diff --git a/src/fs.rs b/src/fs.rs index 0b193bb..ac85def 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -12,7 +12,7 @@ use byteorder::LittleEndian; use byteorder_ext::{ReadBytesExt, WriteBytesExt}; use dir::{Dir, DirRawStream}; -use dir_entry::DIR_ENTRY_SIZE; +use dir_entry::{self, DIR_ENTRY_SIZE}; use file::File; use table::{alloc_cluster, count_free_clusters, read_fat_flags, ClusterIterator}; @@ -303,6 +303,7 @@ impl FsInfoSector { pub struct FsOptions { pub(crate) update_accessed_date: bool, pub(crate) oem_cp_converter: &'static OemCpConverter, + pub(crate) time_provider: &'static TimeProvider, } impl FsOptions { @@ -311,6 +312,7 @@ impl FsOptions { FsOptions { update_accessed_date: false, oem_cp_converter: &LOSSY_OEM_CP_CONVERTER, + time_provider: &DEFAULT_TIME_PROVIDER, } } @@ -325,6 +327,12 @@ impl FsOptions { self.oem_cp_converter = oem_cp_converter; self } + + /// Changes default time provider. + pub fn time_provider(mut self, time_provider: &'static TimeProvider) -> Self { + self.time_provider = time_provider; + self + } } /// A FAT volume statistics. @@ -745,3 +753,37 @@ impl OemCpConverter for LossyOemCpConverter { } pub(crate) static LOSSY_OEM_CP_CONVERTER: LossyOemCpConverter = LossyOemCpConverter { _dummy: () }; + +pub trait TimeProvider { + fn get_current_date(&self) -> dir_entry::Date; + fn get_current_date_time(&self) -> dir_entry::DateTime; +} + +#[derive(Clone)] +pub(crate) struct DefaultTimeProvider { + _dummy: (), +} + +impl TimeProvider for DefaultTimeProvider { + #[cfg(feature = "chrono")] + fn get_current_date(&self) -> dir_entry::Date { + use chrono; + dir_entry::Date::from(chrono::Local::now().date()) + } + #[cfg(not(feature = "chrono"))] + fn get_current_date(&self) -> dir_entry::Date { + dir_entry::Date::decode(0) + } + + #[cfg(feature = "chrono")] + fn get_current_date_time(&self) -> dir_entry::DateTime { + use chrono; + dir_entry::DateTime::from(chrono::Local::now()) + } + #[cfg(not(feature = "chrono"))] + fn get_current_date_time(&self) -> dir_entry::DateTime { + dir_entry::DateTime::decode(0, 0, 0) + } +} + +pub(crate) static DEFAULT_TIME_PROVIDER: DefaultTimeProvider = DefaultTimeProvider { _dummy: () };