455 lines
11 KiB
Rust
455 lines
11 KiB
Rust
use char_enum::{char_enum_derive::FromChar, AsIter, FromChar, FromStrError, ToChar};
|
|
use enumflags2::{bitflags, BitFlag, BitFlags, FromBitsError};
|
|
use std::{error::Error, fmt::Display, num::ParseIntError, ops::BitOr, str::FromStr};
|
|
|
|
use crate::utils::MapWhileRefExt;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct ParseModeError(String);
|
|
|
|
impl ParseModeError {
|
|
pub fn from_bits(bits: &str) -> Self {
|
|
Self(format!("invalid mode: `{bits}`"))
|
|
}
|
|
}
|
|
|
|
impl Display for ParseModeError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl Error for ParseModeError {}
|
|
|
|
impl From<ParseIntError> for ParseModeError {
|
|
fn from(value: ParseIntError) -> Self {
|
|
Self(value.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<FromBitsError<Mode>> for ParseModeError {
|
|
fn from(value: FromBitsError<Mode>) -> Self {
|
|
Self::from_bits(&value.invalid_bits().to_string())
|
|
}
|
|
}
|
|
|
|
#[bitflags]
|
|
#[repr(u16)]
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum Mode {
|
|
IXOTH = 0x0001,
|
|
IWOTH = 0x0002,
|
|
IROTH = 0x0004,
|
|
IXGRP = 0x0010,
|
|
IWGRP = 0x0020,
|
|
IRGRP = 0x0040,
|
|
IXUSR = 0x0100,
|
|
IWUSR = 0x0200,
|
|
IRUSR = 0x0400,
|
|
ISVTX = 0x1000,
|
|
ISGID = 0x2000,
|
|
ISUID = 0x4000,
|
|
}
|
|
|
|
impl Display for Mode {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
pub struct ModeFlags(BitFlags<Mode>);
|
|
|
|
impl FromStr for ModeFlags {
|
|
type Err = ParseModeError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let bits = u16::from_str_radix(s, 16)?;
|
|
|
|
Ok(Self(BitFlags::from_bits(bits)?))
|
|
}
|
|
}
|
|
|
|
impl Display for ModeFlags {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{:X}", self.0)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u16> for ModeFlags {
|
|
type Error = FromBitsError<Mode>;
|
|
|
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
|
Ok(Self(BitFlags::from_bits(value)?))
|
|
}
|
|
}
|
|
|
|
impl From<BitFlags<Mode>> for ModeFlags {
|
|
fn from(value: BitFlags<Mode>) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, FromChar, PartialEq, Eq)]
|
|
pub enum Operator {
|
|
#[value = "-"]
|
|
Sub,
|
|
#[value = "+"]
|
|
Add,
|
|
#[value = "="]
|
|
Eq,
|
|
}
|
|
|
|
impl Operator {
|
|
pub fn split(s: &str) -> Option<(&str, char, &str)> {
|
|
let mut result = None;
|
|
|
|
for d in Operator::iter() {
|
|
if let Some((a, b)) = s.split_once(d) {
|
|
result = Some((a, d, b));
|
|
break;
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
}
|
|
|
|
#[bitflags]
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, FromChar, PartialEq, Eq)]
|
|
pub enum SymbolicMode {
|
|
#[value = 'r']
|
|
Read,
|
|
#[value = 'w']
|
|
Write,
|
|
#[value = 'x']
|
|
Execute,
|
|
#[value = 'X']
|
|
Search,
|
|
#[value = 's']
|
|
SetId,
|
|
#[value = 't']
|
|
Sticky,
|
|
}
|
|
|
|
#[bitflags]
|
|
#[repr(u8)]
|
|
#[derive(Clone, Copy, Debug, FromChar, PartialEq, Eq)]
|
|
pub enum GroupId {
|
|
#[value = 'u']
|
|
User,
|
|
#[value = 'g']
|
|
Group,
|
|
#[value = 'o']
|
|
Other,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub struct Modes<T: BitFlag>(BitFlags<T>);
|
|
|
|
impl<T: BitFlag> Default for Modes<T> {
|
|
fn default() -> Self {
|
|
Modes(BitFlags::<T>::default())
|
|
}
|
|
}
|
|
|
|
impl<T: BitFlag + FromChar> FromStr for Modes<T> {
|
|
type Err = FromStrError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let flags = s
|
|
.chars()
|
|
.map_while_ref(|c: &char| T::from_char(*c))
|
|
.fold(BitFlags::default(), BitOr::bitor);
|
|
|
|
Ok(Self(flags))
|
|
}
|
|
}
|
|
|
|
impl<T: BitFlag + Display> Display for Modes<T> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let mut iter = self.0.iter();
|
|
if let Some(head) = iter.next() {
|
|
write!(f, "{head}")?;
|
|
|
|
for item in iter {
|
|
write!(f, "{item}")?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct ParseSymbolicArgsError(FromStrError);
|
|
|
|
impl Display for ParseSymbolicArgsError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl Error for ParseSymbolicArgsError {}
|
|
|
|
impl From<ParseSymbolicArgsError> for FromStrError {
|
|
fn from(value: ParseSymbolicArgsError) -> Self {
|
|
value.0
|
|
}
|
|
}
|
|
|
|
impl From<FromStrError> for ParseSymbolicArgsError {
|
|
fn from(value: FromStrError) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum SymbolicArgs {
|
|
Group(Modes<GroupId>),
|
|
Mode(Modes<SymbolicMode>),
|
|
}
|
|
|
|
impl FromStr for SymbolicArgs {
|
|
type Err = ParseSymbolicArgsError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match Modes::<SymbolicMode>::from_str(s) {
|
|
Ok(perms) => Ok(Self::Mode(perms)),
|
|
Err(_) => match Modes::<GroupId>::from_str(s) {
|
|
Ok(perms) => Ok(Self::Group(perms)),
|
|
Err(_) => Err(FromStrError::new(format!("{} is not a valid argument", s)).into()),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for SymbolicArgs {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
SymbolicArgs::Group(modes) => write!(f, "{modes}"),
|
|
SymbolicArgs::Mode(modes) => write!(f, "{modes}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum ParseSymbolicError {
|
|
Group(String),
|
|
Operator(String),
|
|
Mode(String),
|
|
}
|
|
|
|
impl Display for ParseSymbolicError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"{}",
|
|
match self {
|
|
Self::Group(e) => e,
|
|
Self::Operator(e) => e,
|
|
Self::Mode(e) => e,
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Error for ParseSymbolicError {}
|
|
|
|
impl From<ParseSymbolicArgsError> for ParseSymbolicError {
|
|
fn from(value: ParseSymbolicArgsError) -> Self {
|
|
Self::Mode(value.to_string())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct SymbolicPermissions {
|
|
groups: Modes<GroupId>,
|
|
operator: Operator,
|
|
mode: SymbolicArgs,
|
|
}
|
|
|
|
impl SymbolicPermissions {
|
|
pub fn new(groups: Modes<GroupId>, operator: Operator, mode: SymbolicArgs) -> Self {
|
|
Self {
|
|
groups,
|
|
operator,
|
|
mode,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for SymbolicPermissions {
|
|
type Err = ParseSymbolicError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match Operator::split(s) {
|
|
Some((groups, operator, mode)) => {
|
|
let groups =
|
|
Modes::from_str(groups).map_err(|e| ParseSymbolicError::Group(e.to_string()));
|
|
|
|
let operator = Operator::from_char(operator)
|
|
.map_err(|o| ParseSymbolicError::Operator(o.to_string()));
|
|
|
|
let mode = SymbolicArgs::from_str(mode)?;
|
|
|
|
Ok(Self::new(groups?, operator?, mode))
|
|
}
|
|
None => Err(ParseSymbolicError::Operator(format!(
|
|
"received invalid permission syntax: {s}"
|
|
))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for SymbolicPermissions {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}{}{}", self.groups, self.operator, self.mode)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct ParseOctalError(String);
|
|
|
|
impl Display for ParseOctalError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl Error for ParseOctalError {}
|
|
|
|
impl From<ParseModeError> for ParseOctalError {
|
|
fn from(value: ParseModeError) -> Self {
|
|
Self(value.0)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct OctalPermissions(ModeFlags);
|
|
|
|
impl Display for OctalPermissions {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl FromStr for OctalPermissions {
|
|
type Err = ParseOctalError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Self(ModeFlags::from_str(s)?))
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u16> for OctalPermissions {
|
|
type Error = FromBitsError<Mode>;
|
|
|
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
|
Ok(Self(ModeFlags::try_from(value)?))
|
|
}
|
|
}
|
|
|
|
impl From<BitFlags<Mode>> for OctalPermissions {
|
|
fn from(value: BitFlags<Mode>) -> Self {
|
|
Self(value.into())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum ParsePermissionsError {
|
|
Symbolic(ParseSymbolicError),
|
|
Octal(ParseOctalError),
|
|
}
|
|
|
|
impl Display for ParsePermissionsError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
ParsePermissionsError::Symbolic(e) => write!(f, "{e}"),
|
|
ParsePermissionsError::Octal(e) => write!(f, "{e}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Error for ParsePermissionsError {}
|
|
|
|
impl From<ParseSymbolicError> for ParsePermissionsError {
|
|
fn from(value: ParseSymbolicError) -> Self {
|
|
Self::Symbolic(value)
|
|
}
|
|
}
|
|
|
|
impl From<ParseOctalError> for ParsePermissionsError {
|
|
fn from(value: ParseOctalError) -> Self {
|
|
Self::Octal(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub enum Permissions {
|
|
Symbolic(SymbolicPermissions),
|
|
Octal(OctalPermissions),
|
|
}
|
|
|
|
impl FromStr for Permissions {
|
|
type Err = ParsePermissionsError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(SymbolicPermissions::from_str(s)
|
|
.map(Self::Symbolic)
|
|
.or_else(|_| OctalPermissions::from_str(s).map(Self::Octal))?)
|
|
}
|
|
}
|
|
|
|
impl Display for Permissions {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Permissions::Symbolic(v) => write!(f, "{v}"),
|
|
Permissions::Octal(v) => write!(f, "{v}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::str::FromStr;
|
|
|
|
use enumflags2::BitFlags;
|
|
|
|
use crate::fs::permission::{
|
|
GroupId, Modes, Operator, SymbolicArgs, SymbolicMode, SymbolicPermissions,
|
|
};
|
|
|
|
use super::{ModeFlags, Permissions};
|
|
|
|
#[test]
|
|
fn mode_flags() {
|
|
let flags = ModeFlags(BitFlags::all());
|
|
assert_eq!(flags.to_string(), "7777");
|
|
}
|
|
|
|
#[test]
|
|
fn symbolic_permissions() {
|
|
let sym = Permissions::from_str("ug+w").unwrap();
|
|
let group_sym = Permissions::from_str("g=u").unwrap();
|
|
|
|
assert_eq!(
|
|
sym,
|
|
Permissions::Symbolic(SymbolicPermissions::new(
|
|
Modes(GroupId::User | GroupId::Group),
|
|
Operator::Add,
|
|
SymbolicArgs::Mode(Modes(BitFlags::from(SymbolicMode::Write)))
|
|
))
|
|
);
|
|
|
|
assert_eq!(
|
|
group_sym,
|
|
Permissions::Symbolic(SymbolicPermissions::new(
|
|
Modes(GroupId::Group.into()),
|
|
Operator::Eq,
|
|
SymbolicArgs::Group(Modes(BitFlags::from(GroupId::User)))
|
|
))
|
|
);
|
|
}
|
|
}
|