master
Avril 4 years ago
parent 87c24ff87d
commit bd1fa997a7
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,13 +1,15 @@
[package] [package]
name = "readable-perms" name = "readable-perms"
description = "More usable UNIX file permissions interface"
tags = ["unix", "fs", "permissions", "file", "filesystem", "file permissions", "linux", "mode_t", "chmod"]
version = "0.1.0" version = "0.1.0"
authors = ["Avril <flanchan@cumallover.me>"] authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018" edition = "2018"
license = "GPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
# Ignore this, it is for regenerating the lookup table used on stable (needed due to lack of matching in `const fn`). # Ignore this, it is for regenerating the lookup table used on stable (needed due to lack of matching in `const fn`).
# It is already included in this repo. # It is already included in this repo.
# If you really want to rebuild it, `cargo test` will output it to `src/stable/mod.rs` lol. # If you really want to rebuild it, `cargo test` will output it to `src/stable/mod.rs` lol.

@ -0,0 +1,82 @@
# Readable Unix Permissions
Data types for the UNIX file permissions model. Convertable to and from `mode_t`.
## Usage
Crate is designed to give finer and more sane control over `mode_t`, so there are multiple ways of using the `Permissions` struct.
All the below functions are `const fn` on nightly.
### Creating from mode
``` rust
let perms = Permissions::from_mask(0o644); // Any value above 0u777 is truncated.
```
### Creating through builder-pattern
You can add masks with `add_mask()`
``` rust
let perms = Permissions::new()
.add_mask(User::Owner, Bit::Read | Bit::Write)
.add_mask(User::Group, Bit::Read);
```
And remove them with `remove_mask()`
``` rust
let perms = Permissions::from_mask(0o777)
.remove_mask(User::Other, Bit::Write | Bit::Execute)
.remove_mask(User::Group, Bit::Write);
```
### Checking
You can check which modes are set with `has_mask()`.
``` rust
let perms = Permissions::from_mask(0o754);
assert!(perms.has_mask(User::Owner, Bit::Mask));
assert!(perms.has_mask(User::Group, Bit::Read | Bit::Execute));
assert!(perms.has_mask(User::Other, Bit::Read));
```
We also derive `PartialEq<u32>`, to compare with `mode_t` directly.
``` rust
let perms = Permissions::from_mask(0o644);
assert_eq!(perms, 0o644);
```
## Extension trait
We also define an extension trait on target family `unix` that follows `std::os::unix::fs::PermissionsExt`.
See [ext.rs] for details.
[ext.rs]: ./src/ext.rs
``` rust
use readable_perms::PermissionsExt as UnixPermsExt;
use std::os::unix::fs::PermissionsExt;
fn do_thing(file: &mut std::fs::File)
{
let perms = file.metadata().unwrap().permissions().unix();
println!("Perms are {}", perms);
}
```
## Performance
On nightly, most functions are `const fn` incuring no runtime cost for constant definitions. On stable, not so. Either way, we define a global `const` lookup table, so that conversions are as fast as a memory lookup.
Adding and removing the masks is usually 1 or two bitwise operations. TODO: Benchmark these?
### Benchmarks
| Type | Value |
|-----------------------|----------------------|
| Conversions from mode | 159 ns/iter (+/- 15) |
| Conversions to mode | 162 ns/iter (+/- 15) |
# License
GPL'd with <3

@ -29,4 +29,3 @@ fn from_mask(b: &mut Bencher)
} }
}); });
} }

@ -179,15 +179,15 @@ bitflags!{
} }
} }
bitflags!{ /*bitflags!{
// not supported, don't care // not supported, don't care
struct StickyBit: u32{ struct StickyBit: u32{
const Empty = 0; const Empty = 0;
const Setuid = 0o40000; const Setuid = 0o40000;
const Setgid = 0o20000; const Setgid = 0o20000;
const SaveSwap = 0o10000; const SaveSwap = 0o10000;
} }
} }*/
/// Permissions struct in readable format /// Permissions struct in readable format
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
@ -212,7 +212,7 @@ pub struct Permissions
/// Read, write, and execute mask for anyone else /// Read, write, and execute mask for anyone else
pub other: Bit, pub other: Bit,
_sticky: StickyBit, //idc about this either u_mask: u32,
} }
/// The class of user that UNIX permissions care about /// The class of user that UNIX permissions care about
@ -287,7 +287,8 @@ impl Permissions
owner: Bit::None, owner: Bit::None,
group: Bit::None, group: Bit::None,
other: Bit::None, other: Bit::None,
_sticky: StickyBit::Empty,
u_mask: 0u32,
} }
} }
} }
@ -309,15 +310,27 @@ impl Permissions
} }
/// Convert into `mode_t` representation. /// Convert into `mode_t` representation.
#[inline] #[cfg(nightly)] pub const fn mask(&self) -> u32 #[inline] #[cfg(nightly)] const fn mask_calc(&self) -> u32
{ {
self.c_owner().mode() | self.c_owner().mode() |
self.c_group().mode() | self.c_group().mode() |
self.c_other().mode() self.c_other().mode()
} }
#[inline] #[cfg(nightly)] const fn into_calced(mut self) -> Self
{
self.u_mask = self.mask_calc();
self
}
/// Convert into `mode_t` representation. /// Convert into `mode_t` representation.
#[inline] #[cfg(not(nightly))] pub fn mask(&self) -> u32 #[inline] pub const fn mask(&self) -> u32
{
self.u_mask
}
/// Convert into `mode_t` representation.
#[inline] #[cfg(not(nightly))] fn mask_calc(&self) -> u32
{ {
self.c_owner().mode() | self.c_owner().mode() |
self.c_group().mode() | self.c_group().mode() |
@ -332,8 +345,8 @@ impl Permissions
group: Class::Group(Bit::Mask).mask_mode(bit), group: Class::Group(Bit::Mask).mask_mode(bit),
other: Class::Other(Bit::Mask).mask_mode(bit), other: Class::Other(Bit::Mask).mask_mode(bit),
_sticky: StickyBit::Empty, u_mask: 0,
} }.into_calced()
} }
/// Convert from `mode_t` representation. /// Convert from `mode_t` representation.
@ -348,7 +361,9 @@ impl Permissions
/// For builder pattern /// For builder pattern
#[inline] #[cfg(nightly)] pub const fn add_mask(self, class: User, bit: Bit) -> Self #[inline] #[cfg(nightly)] pub const fn add_mask(self, class: User, bit: Bit) -> Self
{ {
match class { Self {
u_mask: self.u_mask | class.mode(bit),
..match class {
User::Owner => { User::Owner => {
Self { Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()), owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
@ -369,6 +384,7 @@ impl Permissions
}, },
} }
} }
}
/// Consume and add a mask. /// Consume and add a mask.
/// ///
@ -376,7 +392,9 @@ impl Permissions
/// For builder pattern /// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn add_mask(self, class: User, bit: Bit) -> Self #[inline] #[cfg(not(nightly))] pub fn add_mask(self, class: User, bit: Bit) -> Self
{ {
match class { Self {
u_mask: self.u_mask | class.mode(bit),
..match class {
User::Owner => { User::Owner => {
Self { Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()), owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
@ -386,17 +404,20 @@ impl Permissions
User::Group => { User::Group => {
Self { Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()), group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self ..self
} }
}, },
User::Other => { User::Other => {
Self { Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()), other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self ..self
} }
}, },
} }
} }
}
/// Consume and remove a mask. /// Consume and remove a mask.
@ -405,7 +426,9 @@ impl Permissions
/// For builder pattern /// For builder pattern
#[inline] #[cfg(nightly)] pub const fn remove_mask(self, class: User, bit: Bit) -> Self #[inline] #[cfg(nightly)] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
{ {
match class { Self{
u_mask: self.u_mask & !class.mode(bit),
..match class {
User::Owner => { User::Owner => {
Self { Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()), owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
@ -426,6 +449,7 @@ impl Permissions
}, },
} }
} }
}
/// Consume and remove a mask. /// Consume and remove a mask.
@ -434,7 +458,9 @@ impl Permissions
/// For builder pattern /// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn remove_mask(self, class: User, bit: Bit) -> Self #[inline] #[cfg(not(nightly))] pub fn remove_mask(self, class: User, bit: Bit) -> Self
{ {
match class { Self {
u_mask: self.u_mask & !class.mode(bit),
..match class {
User::Owner => { User::Owner => {
Self { Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()), owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
@ -455,6 +481,7 @@ impl Permissions
}, },
} }
} }
}
/// Returns true if the specified mode mask is all present /// Returns true if the specified mode mask is all present
#[inline] #[cfg(nightly)] pub const fn has_mask(&self, class: User, bit: Bit) -> bool #[inline] #[cfg(nightly)] pub const fn has_mask(&self, class: User, bit: Bit) -> bool
@ -493,7 +520,8 @@ impl From<Permissions> for u32
{ {
#[inline] fn from(from: Permissions) -> Self #[inline] fn from(from: Permissions) -> Self
{ {
from.mask() //can we not do the `MAP` here ;~; debug_assert_eq!(from.mask_calc(), from.mask());
from.mask()
} }
} }
@ -565,7 +593,7 @@ mod output {
print_bit(to,&perm.group)?; print_bit(to,&perm.group)?;
write!(to, ", other: ")?; write!(to, ", other: ")?;
print_bit(to,&perm.other)?; print_bit(to,&perm.other)?;
write!(to, ", _sticky: StickyBit::Empty}}, ")?; write!(to, ", u_mask: {}}}, ", perm.mask_calc())?;
} }
writeln!(to, "]")?; writeln!(to, "]")?;
Ok(()) Ok(())
@ -659,6 +687,6 @@ impl std::fmt::Display for Permissions
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{ {
println!("0{}", self.mask()) write!(f, "0{}", self.mask())
} }
} }

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save