Initial commit

This commit is contained in:
Nathan Fisher 2023-07-02 00:44:39 -04:00
commit 0ddfb168f5
4 changed files with 207 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "pk-rs"
version = "0.1.0"

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "pk-rs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

191
src/lib.rs Normal file
View File

@ -0,0 +1,191 @@
use std::{string::FromUtf8Error, array::TryFromSliceError, fmt, io::{Write, self, Read}, num::TryFromIntError};
#[derive(Debug)]
pub enum Error {
Int(TryFromIntError),
Io(io::Error),
Utf8(FromUtf8Error),
Slice(TryFromSliceError),
UnknownFileType,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(e) => write!(f, "{e}"),
Self::Utf8(e) => write!(f, "{e}"),
Self::Slice(e) => write!(f, "{e}"),
Self::Io(e) => write!(f, "{e}"),
Self::UnknownFileType => write!(f, "unknown file type"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Int(e) => Some(e),
Self::Io(e) => Some(e),
Self::Utf8(e) => Some(e),
Self::Slice(e) => Some(e),
_ => None,
}
}
}
impl From<TryFromIntError> for Error {
fn from(value: TryFromIntError) -> Self {
Self::Int(value)
}
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<FromUtf8Error> for Error {
fn from(value: FromUtf8Error) -> Self {
Self::Utf8(value)
}
}
impl From<TryFromSliceError> for Error {
fn from(value: TryFromSliceError) -> Self {
Self::Slice(value)
}
}
#[derive(Debug)]
pub struct Special {
pub major: u32,
pub minor: u32,
}
impl Special {
pub fn read<T: Read>(reader: &mut T) -> Result<Self, Error> {
let mut buf = [0; 8];
reader.read_exact(&mut buf)?;
let major: [u8; 4] = buf[..4].try_into()?;
let major = u32::from_le_bytes(major);
let minor: [u8; 4] = buf[4..].try_into()?;
let minor = u32::from_le_bytes(minor);
Ok(Self { major, minor })
}
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), Error> {
writer.write_all(&self.major.to_le_bytes())?;
writer.write_all(&self.minor.to_le_bytes())?;
Ok(())
}
}
#[repr(u8)]
#[derive(Debug)]
pub enum FileType {
Normal = 0,
HardLink(String),
SoftLink(String),
Directory,
Character(Special),
Block(Special),
Fifo,
}
impl FileType {
pub fn read<T: Read>(reader: &mut T) -> Result<Self, Error> {
let mut buf = [0; 1];
reader.read_exact(&mut buf)?;
match buf[0] {
0 => Ok(Self::Normal),
1 => {
let mut l = [0; 8];
reader.read_exact(&mut l)?;
let len = u64::from_le_bytes(l);
let mut buf = Vec::with_capacity(len.try_into()?);
let mut handle = reader.take(len);
handle.read(&mut buf)?;
let s = String::from_utf8(buf)?;
Ok(Self::HardLink(s))
},
2 => {
let mut l = [0; 8];
reader.read_exact(&mut l)?;
let len = u64::from_le_bytes(l);
let mut buf = Vec::with_capacity(len.try_into()?);
let mut handle = reader.take(len);
handle.read(&mut buf)?;
let s = String::from_utf8(buf)?;
Ok(Self::SoftLink(s))
},
3 => Ok(Self::Directory),
4 => {
let sp = Special::read(reader)?;
Ok(Self::Character(sp))
},
5 => {
let sp = Special::read(reader)?;
Ok(Self::Block(sp))
},
6 => Ok(Self::Fifo),
_ => Err(Error::UnknownFileType),
}
}
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), Error> {
match self {
Self::Normal => writer.write_all(&[0])?,
Self::HardLink(s) => {
writer.write_all(&[1])?;
let len = s.len() as u64;
writer.write_all(&len.to_le_bytes())?;
writer.write_all(s.as_bytes())?;
},
Self::SoftLink(s) => {
writer.write_all(&[2])?;
let len = s.len() as u64;
writer.write_all(&len.to_le_bytes())?;
writer.write_all(s.as_bytes())?;
},
Self::Directory => writer.write_all(&[3])?,
Self::Character(s) => {
writer.write_all(&[4])?;
s.write(writer)?;
},
Self::Block(s) => {
writer.write_all(&[5])?;
s.write(writer)?;
},
Self::Fifo => writer.write_all(&[6])?,
}
Ok(())
}
}
#[derive(Debug)]
pub struct Header {
pub name: String,
pub filetype: FileType,
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub size: u64,
pub mtime: u64,
}
impl Header {
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), Error> {
let len = self.name.len() as u64;
writer.write_all(&len.to_le_bytes())?;
writer.write_all(self.name.as_bytes())?;
self.filetype.write(writer)?;
[self.mode, self.uid, self.gid].iter().try_for_each(|f| {
writer.write_all(&f.to_le_bytes())
})?;
[self.size, self.mtime].iter().try_for_each(|f| {
writer.write_all(&f.to_le_bytes())
})?;
Ok(())
}
}