use std::fmt;
use nom::IResult;
use nom::number::streaming::be_u32;
use nom::number::streaming::be_u64;
use nom::number::streaming::le_u32;
use nom::number::streaming::le_u64;
use serde::ser::Serialize;
use serde::ser::Serializer;
use serde::ser::SerializeStruct;
#[cfg(test)]
mod arbitrary;
bitflags::bitflags! {
#[derive(Default)]
pub struct FileAttrFlags: u32 {
const Size = 0x00000001;
const UidGid = 0x00000002;
const Permissions = 0x00000004;
const ACModTime = 0x00000008;
const Extended = 0x80000000;
}
}
impl<I: nom_derive::InputSlice> nom_derive::Parse<I> for FileAttrFlags {
fn parse_be(i: I) -> IResult<I, Self> {
let (i, flags) = be_u32(i)?;
let this = Self::from_bits_truncate(flags);
Ok((i, this))
}
fn parse_le(i: I) -> IResult<I, Self> {
let (i, flags) = le_u32(i)?;
let this = Self::from_bits_truncate(flags);
Ok((i, this))
}
fn parse(i: I) -> IResult<I, Self> {
Self::parse_be(i)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
#[repr(transparent)]
pub struct Permissions(u32);
impl fmt::Display for Permissions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}{}{}{}{}{}{}{}{}{}", self.dir(), self.owner_r(), self.owner_w(), self.owner_x(), self.group_r(), self.group_w(), self.group_x(), self.other_r(), self.other_w(), self.other_x())
}
}
impl Permissions {
fn dir(self) -> char {
match self.0 & 0o40000 {
0 => '-',
_ => 'd'
}
}
fn owner_r(self) -> char {
match self.0 & 0o400 {
0 => '-',
_ => 'r'
}
}
fn owner_w(self) -> char {
match self.0 & 0o200 {
0 => '-',
_ => 'w'
}
}
fn owner_x(self) -> char {
match self.0 & 0o100 {
0 => '-',
_ => 'x'
}
}
fn group_r(self) -> char {
match self.0 & 0o040 {
0 => '-',
_ => 'r'
}
}
fn group_w(self) -> char {
match self.0 & 0o020 {
0 => '-',
_ => 'w'
}
}
fn group_x(self) -> char {
match self.0 & 0o010 {
0 => '-',
_ => 'x'
}
}
fn other_r(self) -> char {
match self.0 & 0o004 {
0 => '-',
_ => 'r'
}
}
fn other_w(self) -> char {
match self.0 & 0o002 {
0 => '-',
_ => 'w'
}
}
fn other_x(self) -> char {
match self.0 & 0o001 {
0 => '-',
_ => 'x'
}
} }
impl From<u32> for Permissions {
fn from(raw: u32) -> Self {
Self(raw)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FileAttributes {
pub flags: FileAttrFlags,
pub size: Option<u64>,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub permissions: Option<Permissions>,
pub atime: Option<u32>,
pub mtime: Option<u32>,
}
fn format_month(ts: &time::OffsetDateTime) -> &'static str {
match ts.month() {
time::Month::January => "Jan",
time::Month::February => "Feb",
time::Month::March => "Mar",
time::Month::April => "Apr",
time::Month::May => "May",
time::Month::June => "Jun",
time::Month::July => "Jul",
time::Month::August => "Aug",
time::Month::September => "Sep",
time::Month::October => "Oct",
time::Month::November => "Nov",
time::Month::December => "Dec"
}
}
impl fmt::Display for FileAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self.permissions {
Some(v) => write!(f, "{} ", v)?,
None => write!(f, " ")?
};
write!(f, " 1 ")?;
match self.uid {
Some(v) => write!(f, "{: <8} ", v)?,
None => write!(f, " ")?
};
match self.gid {
Some(v) => write!(f, "{: <8} ", v)?,
None => write!(f, " ")?
};
match self.size {
Some(v) => write!(f, "{: >8} ", v)?,
None => write!(f, " ")?
};
match self.mtime {
Some(v) => {
let time = time::OffsetDateTime::from_unix_timestamp(v.into()).unwrap();
write!(f, "{} {: >2} {:0>2}:{:0>2} ", format_month(&time), time.day(), time.hour(), time.minute())?
},
None => write!(f, " ")?
};
Ok(())
}
}
impl Serialize for FileAttributes {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut field_count = 1;
if(self.flags.contains(FileAttrFlags::Size)) {
field_count += 1;
}
if(self.flags.contains(FileAttrFlags::UidGid)) {
field_count += 2;
}
if(self.flags.contains(FileAttrFlags::Permissions)) {
field_count += 1;
}
if(self.flags.contains(FileAttrFlags::ACModTime)) {
field_count += 2;
}
let mut state = serializer.serialize_struct("FileAttributes", field_count)?;
state.serialize_field("flags", &self.flags.bits())?;
if(self.flags.contains(FileAttrFlags::Size)) {
state.serialize_field("size", &self.size.unwrap_or(0))?;
}
if(self.flags.contains(FileAttrFlags::UidGid)) {
state.serialize_field("uid", &self.uid.unwrap_or(0))?;
state.serialize_field("gid", &self.gid.unwrap_or(0))?;
}
if(self.flags.contains(FileAttrFlags::Permissions)) {
state.serialize_field("permissions", &self.permissions.unwrap_or_default())?;
}
if(self.flags.contains(FileAttrFlags::ACModTime)) {
state.serialize_field("atime", &self.atime.unwrap_or(0))?;
state.serialize_field("mtime", &self.mtime.unwrap_or(0))?;
}
state.end()
} }
impl<I: nom_derive::InputSlice> nom_derive::Parse<I> for FileAttributes {
fn parse_be(i: I) -> IResult<I, Self> {
let (mut i, flags) = FileAttrFlags::parse(i)?;
let mut attrs = Self{
flags,
..Default::default()
};
if(attrs.flags.contains(FileAttrFlags::Size)) {
let (i_inner, size) = be_u64(i)?;
i = i_inner;
attrs.size = Some(size);
}
if(attrs.flags.contains(FileAttrFlags::UidGid)) {
let (i_inner, uid) = be_u32(i)?;
let (i_inner, gid) = be_u32(i_inner)?;
i = i_inner;
attrs.uid = Some(uid);
attrs.gid = Some(gid);
}
if(attrs.flags.contains(FileAttrFlags::Permissions)) {
let (i_inner, permissions) = be_u32(i)?;
i = i_inner;
attrs.permissions = Some(permissions.into());
}
if(attrs.flags.contains(FileAttrFlags::ACModTime)) {
let (i_inner, atime) = be_u32(i)?;
let (i_inner, mtime) = be_u32(i_inner)?;
i = i_inner;
attrs.atime = Some(atime);
attrs.mtime = Some(mtime);
}
Ok((i, attrs))
}
fn parse_le(i: I) -> IResult<I, Self> {
let mut attrs = FileAttributes::default();
let (mut i, flags) = FileAttrFlags::parse(i)?;
attrs.flags = flags;
if(attrs.flags.contains(FileAttrFlags::Size)) {
let (i_inner, size) = le_u64(i)?;
i = i_inner;
attrs.size = Some(size);
}
if(attrs.flags.contains(FileAttrFlags::UidGid)) {
let (i_inner, uid) = le_u32(i)?;
let (i_inner, gid) = le_u32(i_inner)?;
i = i_inner;
attrs.uid = Some(uid);
attrs.gid = Some(gid);
}
if(attrs.flags.contains(FileAttrFlags::Permissions)) {
let (i_inner, permissions) = le_u32(i)?;
i = i_inner;
attrs.permissions = Some(permissions.into());
}
if(attrs.flags.contains(FileAttrFlags::ACModTime)) {
let (i_inner, atime) = le_u32(i)?;
let (i_inner, mtime) = le_u32(i_inner)?;
i = i_inner;
attrs.atime = Some(atime);
attrs.mtime = Some(mtime);
}
Ok((i, attrs))
}
fn parse(i: I) -> IResult<I, Self> {
Self::parse_be(i)
}
}
impl FileAttributes {
pub fn new() -> Self {
Self{
flags: FileAttrFlags::from_bits_truncate(0),
..Default::default()
}
}
pub fn set_size(&mut self, size: u64) {
self.flags.set(FileAttrFlags::Size, true);
self.size = Some(size);
}
pub fn get_uid_gid(&self) -> Option<(u32, u32)> {
match self.flags.contains(FileAttrFlags::UidGid) {
true => Some((self.uid.unwrap(), self.gid.unwrap())),
false => None
}
}
pub fn set_uid_gid(&mut self, uid: u32, gid: u32) {
self.flags.set(FileAttrFlags::UidGid, true);
self.uid = Some(uid);
self.gid = Some(gid);
}
pub fn get_permissions(&self) -> Option<u32> {
self.permissions.map(|p| p.0)
}
pub fn set_permissions(&mut self, permissions: Permissions) {
self.flags.set(FileAttrFlags::Permissions, true);
self.permissions = Some(permissions);
}
pub fn get_atime_mtime(&self) -> Option<(u32, u32)> {
match self.flags.contains(FileAttrFlags::ACModTime) {
true => Some((self.atime.unwrap(), self.mtime.unwrap())),
false => None
}
}
pub fn set_atime_mtime(&mut self, atime: u32, mtime: u32) {
self.flags.set(FileAttrFlags::ACModTime, true);
self.atime = Some(atime);
self.mtime = Some(mtime);
}
pub fn binsize(&self) -> u32 {
let mut size = 4;
if(self.flags.contains(FileAttrFlags::Size)) {
size += 8;
}
if(self.flags.contains(FileAttrFlags::UidGid)) {
size += 8;
}
if(self.flags.contains(FileAttrFlags::Permissions)) {
size += 4;
}
if(self.flags.contains(FileAttrFlags::ACModTime)) {
size += 8;
}
size
} }
impl From<&super::Metadata> for FileAttributes {
fn from(metadata: &super::Metadata) -> Self {
let mut this = Self::new();
this.set_size(metadata.size);
this.set_uid_gid(metadata.uid, metadata.gid);
this.set_permissions({
let mut permissions = metadata.permissions;
if(metadata.is_dir) {
permissions |= 0o40000;
}
permissions.into()
});
this.set_atime_mtime(metadata.atime.unix_timestamp() as u32, metadata.mtime.unix_timestamp() as u32);
this
}
}
impl From<super::Metadata> for FileAttributes {
fn from(metadata: super::Metadata) -> Self {
Self::from(&metadata)
}
}