use super::{Dehydrate, Hydrate, dehydrate};
use std::io::Cursor;
pub type Type = u8;
pub type Tag = u16;
pub type Fid = u32;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct OpenMode(u8);
impl From<OpenMode> for u8 {
fn from(v: OpenMode) -> Self {
v.0
}
}
impl From<u8> for OpenMode {
fn from(v: u8) -> Self {
OpenMode(v)
}
}
impl<T> Hydrate<T> for OpenMode
where
Self: Sized,
T: AsRef<[u8]>,
{
type Error = std::io::Error;
fn hydrate(b: &mut Cursor<T>) -> Result<Self, Self::Error> {
Ok(u8::hydrate(b)?.into())
}
}
impl Dehydrate for OpenMode
where
Self: Sized,
{
type Error = std::io::Error;
fn dehydrate(&self, b: &mut Cursor<Vec<u8>>) -> Result<(), Self::Error> {
let raw: u8 = (*self).into();
dehydrate!(b, raw);
Ok(())
}
}
pub enum IoDirection {
Read,
Write,
ReadWrite,
}
impl OpenMode {
pub const fn direction(&self) -> IoDirection {
match self.0 % 0x04 {
0 => IoDirection::Read,
1 => IoDirection::Write,
2 => IoDirection::ReadWrite,
3 => IoDirection::Read,
_ => unreachable!(),
}
}
pub const fn execute(&self) -> bool {
self.0 & 0x03 == 0x03
}
pub const fn truncate(&self) -> bool {
self.0 & 0x10 == 0x10
}
pub const fn remove(&self) -> bool {
self.0 & 0x40 == 0x40
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FileType {
Dir,
Append,
Excl,
Auth,
Tmp,
Link,
Device,
NamedPipe,
Socket,
File,
Unknown(u8),
}
impl From<std::fs::Metadata> for FileType {
fn from(v: std::fs::Metadata) -> Self {
if v.is_file() {
return Self::File;
}
if v.is_dir() {
return Self::Dir;
}
if v.is_symlink() {
return Self::Link;
}
Self::Unknown(0)
}
}
impl From<u8> for FileType {
fn from(v: u8) -> FileType {
match v {
0x80 => FileType::Dir,
0x40 => FileType::Append,
0x20 => FileType::Excl,
0x08 => FileType::Auth,
0x04 => FileType::Tmp,
0x02 => FileType::Link,
0x00 => FileType::File,
_ => FileType::Unknown(v),
}
}
}
impl From<FileType> for u32 {
fn from(qt: FileType) -> u32 {
let v: u8 = qt.into();
let v: u32 = v.into();
let v = v << (32 - 8);
v | match qt {
FileType::Device => 0x00800000,
FileType::NamedPipe => 0x00200000,
FileType::Socket => 0x00100000,
_ => 0,
}
}
}
impl From<u32> for FileType {
fn from(v: u32) -> FileType {
let v = v & 0xfffffe00;
match v {
0x00800000 => FileType::Device,
0x00200000 => FileType::NamedPipe,
0x00100000 => FileType::Socket,
_ => {
let v: u8 = (v >> (32 - 8)).try_into().unwrap();
v.into()
}
}
}
}
impl From<FileType> for u8 {
fn from(qt: FileType) -> u8 {
match qt {
FileType::Dir => 0x80,
FileType::Append => 0x40,
FileType::Excl => 0x20,
FileType::Auth => 0x08,
FileType::Tmp => 0x04,
FileType::Link => 0x02,
FileType::File => 0x00,
FileType::Unknown(v) => v,
_ => 0x00,
}
}
}
impl<T> Hydrate<T> for FileType
where
Self: Sized,
T: AsRef<[u8]>,
{
type Error = std::io::Error;
fn hydrate(b: &mut Cursor<T>) -> Result<Self, Self::Error> {
Ok(u8::hydrate(b)?.into())
}
}
impl Dehydrate for FileType
where
Self: Sized,
{
type Error = std::io::Error;
fn dehydrate(&self, b: &mut Cursor<Vec<u8>>) -> Result<(), Self::Error> {
let raw: u8 = (*self).into();
dehydrate!(b, raw);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Qid {
pub ty: FileType,
pub version: u32,
pub path: u64,
}
impl Qid {
pub fn new(ty: FileType, version: u32, path: u64) -> Qid {
Qid { ty, version, path }
}
}
impl<T> Hydrate<T> for Qid
where
Self: Sized,
T: AsRef<[u8]>,
{
type Error = std::io::Error;
fn hydrate(b: &mut Cursor<T>) -> Result<Self, Self::Error> {
Ok(Qid::new(
FileType::hydrate(b)?,
u32::hydrate(b)?,
u64::hydrate(b)?,
))
}
}
impl Dehydrate for Qid
where
Self: Sized,
{
type Error = std::io::Error;
fn dehydrate(&self, b: &mut Cursor<Vec<u8>>) -> Result<(), Self::Error> {
dehydrate!(b, self.ty, self.version, self.path);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{super::test_round_trip, Dehydrate, FileType, Hydrate, Qid};
use std::io::Cursor;
test_round_trip!(
round_trip_qid,
Qid,
Qid,
(Qid::new(FileType::File, 10, 0xF00CAFE))
);
#[test]
fn test_filetype() {
for (ft, check) in [
(FileType::File, 0u8),
(FileType::Dir, 0x80),
(FileType::Append, 0x40),
(FileType::Excl, 0x20),
(FileType::Auth, 0x08),
(FileType::Tmp, 0x04),
(FileType::Link, 0x02),
(FileType::Device, 0x00),
(FileType::NamedPipe, 0x00),
(FileType::Socket, 0x00),
] {
assert_eq!(check, ft.into());
}
for (ft, check) in [
(FileType::File, 0u32),
(FileType::Dir, 0x80000000),
(FileType::Append, 0x40000000),
(FileType::Excl, 0x20000000),
(FileType::Auth, 0x08000000),
(FileType::Tmp, 0x04000000),
(FileType::Link, 0x02000000),
(FileType::Device, 0x00800000),
(FileType::NamedPipe, 0x00200000),
(FileType::Socket, 0x00100000),
] {
let ftu: u32 = ft.into();
assert_eq!(check, ftu);
assert_eq!(ft, ftu.into());
}
}
}