a2fuse 0.1.3

Mount and maintain Apple II ProDOS disk images
Documentation
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum StorageType {
    Deleted = 0x0,
    Seedling = 0x1,
    Sapling = 0x2,
    Tree = 0x3,
    PascalArea = 0x4,
    Extended = 0x5,
    Subdirectory = 0xd,
    SubdirectoryHeader = 0xe,
    VolumeHeader = 0xf,
}

impl StorageType {
    pub fn from_nibble(value: u8) -> Option<Self> {
        Some(match value {
            0x0 => Self::Deleted,
            0x1 => Self::Seedling,
            0x2 => Self::Sapling,
            0x3 => Self::Tree,
            0x4 => Self::PascalArea,
            0x5 => Self::Extended,
            0xd => Self::Subdirectory,
            0xe => Self::SubdirectoryHeader,
            0xf => Self::VolumeHeader,
            _ => return None,
        })
    }

    pub fn is_regular_file(self) -> bool {
        matches!(self, Self::Seedling | Self::Sapling | Self::Tree)
    }
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct AccessFlags(pub u8);

impl AccessFlags {
    pub const DESTROY: u8 = 0x80;
    pub const RENAME: u8 = 0x40;
    pub const BACKUP: u8 = 0x20;
    pub const INVISIBLE: u8 = 0x04;
    pub const WRITE: u8 = 0x02;
    pub const READ: u8 = 0x01;

    pub fn readable(self) -> bool {
        self.0 & Self::READ != 0
    }

    pub fn writable(self) -> bool {
        self.0 & Self::WRITE != 0
    }
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ProdosTimestamp {
    pub year: u16,
    pub month: u8,
    pub day: u8,
    pub hour: u8,
    pub minute: u8,
}

impl ProdosTimestamp {
    pub fn decode(date: u16, time: u16) -> Option<Self> {
        if date == 0 && time == 0 {
            return None;
        }

        let short_year = (date >> 9) & 0x7f;
        let year = if short_year < 40 {
            2000 + short_year
        } else {
            1900 + short_year
        };
        let month = ((date >> 5) & 0x0f) as u8;
        let day = (date & 0x1f) as u8;
        let hour = ((time >> 8) & 0x1f) as u8;
        let minute = (time & 0x3f) as u8;

        if !(1..=12).contains(&month) || !(1..=31).contains(&day) || hour > 23 || minute > 59 {
            return None;
        }

        Some(Self {
            year,
            month,
            day,
            hour,
            minute,
        })
    }
}