haggis-rs/src/filetype.rs

156 lines
4.2 KiB
Rust

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<u8> for Flag {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
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<T: Read>(reader: &mut T, flag: Flag) -> Result<Self, Error> {
match flag {
Flag::Normal => {
let file = File::read(reader)?;
Ok(Self::Normal(file))
}
Flag::HardLink => {
let s = crate::load_string(reader)?;
Ok(Self::HardLink(s))
}
Flag::SoftLink => {
let s = crate::load_string(reader)?;
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<T: Write>(&self, writer: &mut T) -> Result<(), Error> {
match self {
Self::Normal(f) => f.write(writer)?,
Self::HardLink(s) | Self::SoftLink(s) => {
let len = u16::try_from(s.len())?;
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);
}
}