Add TimeProvider trait and time_provider option

This functionality is very useful for embedded usage where chrono feature
cannot be enabled.
This commit is contained in:
Rafał Harabień 2018-06-28 18:43:02 +02:00
parent 30f3f96537
commit 1f8f8365d6
5 changed files with 59 additions and 62 deletions

View File

@ -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

View File

@ -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<u32>) -> 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
}

View File

@ -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<T: ReadWriteSeek>(&mut self, fs: &FileSystem<T>) -> io::Result<()> {
if self.dirty {
self.write(fs)?;

View File

@ -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)
}

View File

@ -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: () };