use { crate::{Error, File, Special}, std::io::{Read, Write}, }; #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq)] pub(crate) enum Flag { Normal = 0, HardLink = 1, SoftLink = 2, Directory = 3, Character = 4, Block = 5, Fifo = 6, Eof = 7, } impl TryFrom for Flag { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::Normal), 1 => Ok(Self::HardLink), 2 => Ok(Self::SoftLink), 3 => Ok(Self::Directory), 4 => Ok(Self::Character), 5 => Ok(Self::Block), 6 => Ok(Self::Fifo), 7 => Ok(Self::Eof), _ => Err(Error::UnknownFileType), } } } impl From<&FileType> for Flag { fn from(value: &FileType) -> Self { match value { FileType::Normal(_) => Self::Normal, FileType::HardLink(_) => Self::HardLink, FileType::SoftLink(_) => Self::SoftLink, FileType::Directory => Self::Directory, FileType::Character(_) => Self::Character, FileType::Block(_) => Self::Block, FileType::Fifo => Self::Fifo, FileType::Eof => Self::Eof, } } } impl Flag { pub(crate) fn append_mode(&self, mode: u16) -> u16 { let mask: u16 = u16::from(*self as u8) << 13; mode | mask } pub(crate) fn extract_from_raw(raw: u16) -> Result<(Self, u16), Error> { let mask: u16 = 0b111 << 13; let mode = raw & 0o777; let flag: u8 = ((raw & mask) >> 13).try_into()?; let flag: Self = flag.try_into()?; Ok((flag, mode)) } } /// An enum representing the type of file of an archive member #[derive(Debug)] pub enum FileType { /// A normal file and it's contained data Normal(File), /// A hard link to another file in this archive HardLink(String), /// A soft link to another file SoftLink(String), /// A directory inode Directory, /// A character (buffered) device Character(Special), /// A block device Block(Special), /// A Unix named pipe (fifo) Fifo, /// End of archive Eof, } impl FileType { pub(crate) fn read(reader: &mut T, flag: Flag) -> Result { match flag { Flag::Normal => { let file = File::read(reader)?; Ok(Self::Normal(file)) } Flag::HardLink => { let mut len = [0; 2]; reader.read_exact(&mut len)?; let len = u16::from_le_bytes(len); let mut buf = Vec::with_capacity(len.into()); let mut handle = reader.take(len.into()); handle.read_to_end(&mut buf)?; let s = String::from_utf8(buf)?; Ok(Self::HardLink(s)) } Flag::SoftLink => { let mut len = [0; 2]; reader.read_exact(&mut len)?; let len = u16::from_le_bytes(len); let mut buf = Vec::with_capacity(len.into()); let mut handle = reader.take(len.into()); handle.read_to_end(&mut buf)?; let s = String::from_utf8(buf)?; Ok(Self::SoftLink(s)) } Flag::Directory => Ok(Self::Directory), Flag::Character => { let sp = Special::read(reader)?; Ok(Self::Character(sp)) } Flag::Block => { let sp = Special::read(reader)?; Ok(Self::Block(sp)) } Flag::Fifo => Ok(Self::Fifo), Flag::Eof => Ok(Self::Eof), } } pub(crate) fn write(&self, writer: &mut T) -> Result<(), Error> { match self { Self::Normal(f) => f.write(writer)?, Self::HardLink(s) | Self::SoftLink(s) => { let len = s.len() as u16; writer.write_all(&len.to_le_bytes())?; writer.write_all(s.as_bytes())?; } Self::Character(s) | Self::Block(s) => s.write(writer)?, _ => {} } Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn manipulate_mode_softlink() { let mode: u16 = 0o644; let flag = Flag::SoftLink; let raw = flag.append_mode(mode); let (f, m) = Flag::extract_from_raw(raw).unwrap(); assert_eq!(m, mode); assert_eq!(f, flag); } #[test] fn manipulate_mode_normal() { let mode: u16 = 0o755; let flag = Flag::Normal; let raw = flag.append_mode(mode); let (f, m) = Flag::extract_from_raw(raw).unwrap(); assert_eq!(m, mode); assert_eq!(f, flag); } }