simple-fatfs 0.1.0-alpha.2

A simple-to-use FAT filesystem library for Rust (mainly targeted at embedded systems)
Documentation
use core::num;

use crate::time::EPOCH;

use ::time;
use bincode::{impl_borrow_decode, Decode, Encode};
use bitfield_struct::bitfield;
use time::{Date, PrimitiveDateTime, Time};

#[bitfield(u16)]
#[derive(Encode, Decode)]
pub(crate) struct TimeAttribute {
    /// Multiply by 2
    #[bits(5)]
    seconds: u8,
    #[bits(6)]
    minutes: u8,
    #[bits(5)]
    hour: u8,
}

impl From<Time> for TimeAttribute {
    fn from(value: Time) -> Self {
        Self::new()
            .with_seconds(value.second() / 2)
            .with_minutes(value.minute())
            .with_hour(value.hour())
    }
}

#[bitfield(u16)]
#[derive(Encode, Decode)]
pub(crate) struct DateAttribute {
    #[bits(5)]
    day: u8,
    #[bits(4)]
    month: u8,
    #[bits(7)]
    year: u8,
}

impl From<Date> for DateAttribute {
    fn from(value: Date) -> Self {
        Self::new()
            .with_day(value.day())
            .with_month(value.month().into())
            .with_year(
                u8::try_from(value.year() - EPOCH.year())
                    .expect("TODO: proper time handling for such a case"),
            )
    }
}

impl TryFrom<TimeAttribute> for Time {
    type Error = ();

    fn try_from(value: TimeAttribute) -> Result<Self, Self::Error> {
        time::parsing::Parsed::new()
            .with_hour_24(value.hour())
            .and_then(|parsed| parsed.with_minute(value.minutes()))
            .and_then(|parsed| parsed.with_second(value.seconds() * 2))
            .and_then(|parsed| parsed.try_into().ok())
            .ok_or(())
    }
}

impl TryFrom<DateAttribute> for Date {
    type Error = ();

    fn try_from(value: DateAttribute) -> Result<Self, Self::Error> {
        time::parsing::Parsed::new()
            .with_year(i32::from(value.year()) + EPOCH.year())
            .and_then(|parsed| parsed.with_month(value.month().try_into().ok()?))
            .and_then(|parsed| parsed.with_day(num::NonZeroU8::new(value.day())?))
            .and_then(|parsed| parsed.try_into().ok())
            .ok_or(())
    }
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct EntryCreationTime(Option<CreationTime>);

#[derive(Encode, Decode, Debug, Clone, Copy)]
pub(crate) struct CreationTime {
    pub(crate) hundredths_of_second: u8,
    pub(crate) time: TimeAttribute,
    pub(crate) date: DateAttribute,
}

impl<Context> bincode::Decode<Context> for EntryCreationTime {
    fn decode<D: bincode::de::Decoder<Context = Context>>(
        decoder: &mut D,
    ) -> Result<Self, bincode::error::DecodeError> {
        let hundredths_of_second = <u8 as bincode::Decode<Context>>::decode(decoder)?;
        let time = <u16 as bincode::Decode<Context>>::decode(decoder)?;
        let date = <u16 as bincode::Decode<Context>>::decode(decoder)?;

        Ok(Self(if time == 0 || date == 0 {
            None
        } else {
            Some(CreationTime {
                hundredths_of_second,
                time: TimeAttribute::from_bits(time),
                date: DateAttribute::from_bits(date),
            })
        }))
    }
}

impl_borrow_decode!(EntryCreationTime);

impl bincode::Encode for EntryCreationTime {
    fn encode<E: bincode::enc::Encoder>(
        &self,
        encoder: &mut E,
    ) -> Result<(), bincode::error::EncodeError> {
        match self.0 {
            Some(creation_time) => {
                bincode::Encode::encode(&creation_time, encoder)?;
            }
            None => {
                bincode::Encode::encode(&0_u8, encoder)?;
                bincode::Encode::encode(&0_u16, encoder)?;
                bincode::Encode::encode(&0_u16, encoder)?;
            }
        }

        Ok(())
    }
}

impl TryFrom<EntryCreationTime> for Option<PrimitiveDateTime> {
    type Error = ();

    fn try_from(value: EntryCreationTime) -> Result<Self, Self::Error> {
        match value.0 {
            Some(creation_time) => {
                let mut time: Time = creation_time.time.try_into()?;

                let new_seconds = time.second() + creation_time.hundredths_of_second / 100;
                let milliseconds = u16::from(creation_time.hundredths_of_second) % 100 * 10;
                time = time
                    .replace_second(new_seconds)
                    .map_err(|_| ())?
                    .replace_millisecond(milliseconds)
                    .map_err(|_| ())?;

                let date: Date = creation_time.date.try_into()?;

                Ok(Some(PrimitiveDateTime::new(date, time)))
            }
            None => Ok(None),
        }
    }
}

impl From<PrimitiveDateTime> for EntryCreationTime {
    fn from(value: PrimitiveDateTime) -> Self {
        Self(Some(CreationTime {
            hundredths_of_second: (value.second() % 2) * 100
                + u8::try_from(value.millisecond() / 10).expect("this will be in the range 0..100"),
            time: value.time().into(),
            date: value.date().into(),
        }))
    }
}

impl From<Option<PrimitiveDateTime>> for EntryCreationTime {
    fn from(value: Option<PrimitiveDateTime>) -> Self {
        match value {
            Some(value) => value.into(),
            None => Self(None),
        }
    }
}

#[derive(Encode, Decode, Debug, Clone, Copy)]
pub(crate) struct EntryModificationTime {
    pub(crate) time: TimeAttribute,
    pub(crate) date: DateAttribute,
}

impl TryFrom<EntryModificationTime> for PrimitiveDateTime {
    type Error = ();

    fn try_from(value: EntryModificationTime) -> Result<Self, Self::Error> {
        Ok(PrimitiveDateTime::new(
            value.date.try_into()?,
            value.time.try_into()?,
        ))
    }
}

impl From<PrimitiveDateTime> for EntryModificationTime {
    fn from(value: PrimitiveDateTime) -> Self {
        Self {
            time: value.time().into(),
            date: value.date().into(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct EntryLastAccessedTime(Option<DateAttribute>);

impl<Context> bincode::Decode<Context> for EntryLastAccessedTime {
    fn decode<D: bincode::de::Decoder<Context = Context>>(
        decoder: &mut D,
    ) -> Result<Self, bincode::error::DecodeError> {
        let bits = <u16 as bincode::Decode<Context>>::decode(decoder)?;

        Ok(Self(if bits == 0 {
            None
        } else {
            Some(DateAttribute::from_bits(bits))
        }))
    }
}

impl_borrow_decode!(EntryLastAccessedTime);

impl bincode::Encode for EntryLastAccessedTime {
    fn encode<E: bincode::enc::Encoder>(
        &self,
        encoder: &mut E,
    ) -> Result<(), bincode::error::EncodeError> {
        let bits = match self.0 {
            Some(date) => date.into_bits(),
            None => 0,
        };

        bincode::Encode::encode(&bits, encoder)?;

        Ok(())
    }
}

impl TryFrom<EntryLastAccessedTime> for Option<Date> {
    type Error = ();

    fn try_from(value: EntryLastAccessedTime) -> Result<Self, Self::Error> {
        value.0.map(|date| date.try_into()).transpose()
    }
}

impl From<Date> for EntryLastAccessedTime {
    fn from(value: Date) -> Self {
        Self(Some(value.into()))
    }
}

impl From<Option<Date>> for EntryLastAccessedTime {
    fn from(value: Option<Date>) -> Self {
        match value {
            Some(value) => value.into(),
            None => Self(None),
        }
    }
}