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

@ -1,13 +1,15 @@
[package]
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"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
license = "GPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
# 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.
# 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!{
// not supported, don't care
struct StickyBit: u32{
const Empty = 0;
const Setuid = 0o40000;
const Setgid = 0o20000;
const SaveSwap = 0o10000;
/*bitflags!{
// not supported, don't care
struct StickyBit: u32{
const Empty = 0;
const Setuid = 0o40000;
const Setgid = 0o20000;
const SaveSwap = 0o10000;
}
}
}*/
/// Permissions struct in readable format
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
@ -212,7 +212,7 @@ pub struct Permissions
/// Read, write, and execute mask for anyone else
pub other: Bit,
_sticky: StickyBit, //idc about this either
u_mask: u32,
}
/// The class of user that UNIX permissions care about
@ -287,7 +287,8 @@ impl Permissions
owner: Bit::None,
group: Bit::None,
other: Bit::None,
_sticky: StickyBit::Empty,
u_mask: 0u32,
}
}
}
@ -309,15 +310,27 @@ impl Permissions
}
/// 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_group().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.
#[inline] pub const fn mask(&self) -> u32
{
self.u_mask
}
/// Convert into `mode_t` representation.
#[inline] #[cfg(not(nightly))] pub fn mask(&self) -> u32
#[inline] #[cfg(not(nightly))] fn mask_calc(&self) -> u32
{
self.c_owner().mode() |
self.c_group().mode() |
@ -332,8 +345,8 @@ impl Permissions
group: Class::Group(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.
@ -346,27 +359,30 @@ impl Permissions
///
/// # Usage
/// 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 {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
Self {
u_mask: self.u_mask | class.mode(bit),
..match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
}
}
}
@ -376,25 +392,30 @@ impl Permissions
/// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn add_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
Self {
u_mask: self.u_mask | class.mode(bit),
..match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
..self
}
},
}
}
}
@ -405,25 +426,28 @@ impl Permissions
/// For builder pattern
#[inline] #[cfg(nightly)] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
Self{
u_mask: self.u_mask & !class.mode(bit),
..match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
}
}
}
@ -434,25 +458,28 @@ impl Permissions
/// For builder pattern
#[inline] #[cfg(not(nightly))] pub fn remove_mask(self, class: User, bit: Bit) -> Self
{
match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
Self {
u_mask: self.u_mask & !class.mode(bit),
..match class {
User::Owner => {
Self {
owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
..self
}
},
User::Group => {
Self {
group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
..self
}
},
User::Other => {
Self {
other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
..self
}
},
}
}
}
@ -493,7 +520,8 @@ impl From<Permissions> for u32
{
#[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)?;
write!(to, ", other: ")?;
print_bit(to,&perm.other)?;
write!(to, ", _sticky: StickyBit::Empty}}, ")?;
write!(to, ", u_mask: {}}}, ", perm.mask_calc())?;
}
writeln!(to, "]")?;
Ok(())
@ -659,6 +687,6 @@ impl std::fmt::Display for Permissions
{
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