umask-1.0.0/.cargo_vcs_info.json0000644000000001121366113407700122520ustar00{ "git": { "sha1": "89e53b6787dc22ee1f8fe842154992f7c85ea9f5" } } umask-1.0.0/.github/workflows/rust.yml010064400017500001750000000003301362103016700161610ustar0000000000000000name: Rust on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose umask-1.0.0/.gitignore010064400017500001750000000005001362103016400130300ustar0000000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk umask-1.0.0/Cargo.lock0000644000000002111366113407700102250ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "umask" version = "1.0.0" umask-1.0.0/Cargo.toml0000644000000015251366113407700102610ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "umask" version = "1.0.0" authors = ["dystroy "] description = "utility to deal with unix access mode" readme = "README.md" keywords = ["unix", "umask", "permission", "mode"] categories = [] license = "MIT" repository = "https://github.com/Canop/umask" [dependencies] umask-1.0.0/Cargo.toml.orig010064400017500001750000000005101366113375500137460ustar0000000000000000[package] name = "umask" version = "1.0.0" authors = ["dystroy "] repository = "https://github.com/Canop/umask" description = "utility to deal with unix access mode" edition = "2018" keywords = ["unix", "umask", "permission", "mode"] license = "MIT" categories = [] readme = "README.md" [dependencies] umask-1.0.0/LICENSE010064400017500001750000000020461362103016400120540ustar0000000000000000MIT License Copyright (c) 2019 Canop Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. umask-1.0.0/README.md010064400017500001750000000027441362103016400123330ustar0000000000000000[![MIT][s2]][l2] [![Latest Version][s1]][l1] [![docs][s3]][l3] [![Chat on Miaou][s4]][l4] [s1]: https://img.shields.io/crates/v/umask.svg [l1]: https://crates.io/crates/umask [s2]: https://img.shields.io/badge/license-MIT-blue.svg [l2]: LICENSE [s3]: https://docs.rs/umask/badge.svg [l3]: https://docs.rs/umask/ [s4]: https://miaou.dystroy.org/static/shields/room.svg [l4]: https://miaou.dystroy.org/3 # umask A light utility helping with unix mode representation, with strong types to avoid misusing constants. The Mode struct implements `Display` and prints as `"rwxrwxrwx"` ### Import In Cargo.toml: umask = "0.1" ### Usage ``` use umask::*; // You can build from a number: assert_eq!("rw-r--r--", Mode::from(0b110100100).to_string()); assert_eq!("rw-r--r--", Mode::from(0o644).to_string()); // or from a path: let mode = Mode::try_from(&path)?; // You may use `|` to combine class permissions: let mu = Mode::from(0o600); let mo = Mode::from(0o004); let muo = mu | mo; assert_eq!("rw----r--", muo.to_string()); // You can build with semantic constructs: let m = Mode::all() .without(ALL_EXEC); assert_eq!("rw-rw-rw-", m.to_string()); let mut m = Mode::new() .with_class_perm(ALL, READ) .with_class_perm(USER, WRITE); assert_eq!("rw-r--r--", m.to_string()); // Or if you like m |= ALL_EXEC; assert_eq!("rwxr-xr-x", m.to_string()); let m = ALL_READ | USER_WRITE; assert_eq!("rw-r--r--", m.to_string()); // you may test a bit or bitset assert_eq!(m.has(OTHERS_EXEC), false); ``` umask-1.0.0/examples/ls/main.rs010064400017500001750000000011761366113340200146030ustar0000000000000000/// display the content of the current directory use { std::{env, io, path::PathBuf}, umask::Mode, }; fn list_files() -> io::Result<()> { let root = env::current_dir()?; println!("Current dir: {}", root.to_string_lossy()); let mut paths: Vec = root.read_dir()? .filter_map(|e| e.ok()) .map(|e| e.path()) .collect(); paths.sort_unstable(); for path in paths { let mode = Mode::try_from(&path)?; let name = path.file_name().unwrap().to_string_lossy(); println!("{} {}", mode, name); } Ok(()) } fn main() -> io::Result<()> { list_files() } umask-1.0.0/src/lib.rs010064400017500001750000000022031362103016400127450ustar0000000000000000//! A light utility helping with unix mode representation, with strong //! types to avoid misusing constants. //! //! The Mode struct implements `Display` and prints as `"rwxrwxrwx"` //! //! ``` //! use umask::*; //! //! // You can build from a number: //! assert_eq!("rw-r--r--", Mode::from(0b110100100).to_string()); //! assert_eq!("rw-r--r--", Mode::from(0o644).to_string()); //! //! // You may use `|` to combine class permissions: //! let mu = Mode::from(0o640); //! let mo = Mode::from(0o044); //! assert_eq!("rw-r--r--", (mu | mo).to_string()); //! assert_eq!("---r-----", (mu & mo).to_string()); //! //! // You can use more semantic constructs: //! let m = Mode::all() //! .without(ALL_EXEC); //! assert_eq!("rw-rw-rw-", m.to_string()); //! let mut m = Mode::new() //! .with_class_perm(ALL, READ) //! .with_class_perm(USER, WRITE); //! assert_eq!("rw-r--r--", m.to_string()); //! // (semantic functions can be used in const declarations) //! //! // Or //! m |= ALL_EXEC; //! assert_eq!("rwxr-xr-x", m.to_string()); //! let m = ALL_READ | USER_WRITE; //! assert_eq!("rw-r--r--", m.to_string()); //! //! ``` mod mode; pub use mode::*; umask-1.0.0/src/mode.rs010064400017500001750000000135701366113370400131440ustar0000000000000000use std::{ fmt::{self, Display, Formatter, Write}, io, ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}, path::Path, }; #[cfg(unix)] use std::fs; #[cfg(unix)] use std::os::unix::fs::MetadataExt; pub type Class = u32; pub const USER: Class = 0b111000000; pub const GROUP: Class = 0b000111000; pub const OTHERS: Class = 0b000000111; pub const ALL: Class = 0b111111111; pub type Permission = u32; pub const READ: Permission = 0b100100100; pub const WRITE: Permission = 0b010010010; pub const EXEC: Permission = 0b001001001; pub const USER_READ: Mode = Mode::new().with_class_perm(USER, READ); pub const USER_WRITE: Mode = Mode::new().with_class_perm(USER, WRITE); pub const USER_EXEC: Mode = Mode::new().with_class_perm(USER, EXEC); pub const GROUP_READ: Mode = Mode::new().with_class_perm(GROUP, READ); pub const GROUP_WRITE: Mode = Mode::new().with_class_perm(GROUP, WRITE); pub const GROUP_EXEC: Mode = Mode::new().with_class_perm(GROUP, EXEC); pub const OTHERS_READ: Mode = Mode::new().with_class_perm(OTHERS, READ); pub const OTHERS_WRITE: Mode = Mode::new().with_class_perm(OTHERS, WRITE); pub const OTHERS_EXEC: Mode = Mode::new().with_class_perm(OTHERS, EXEC); pub const ALL_READ: Mode = Mode::new().with_class_perm(ALL, READ); pub const ALL_WRITE: Mode = Mode::new().with_class_perm(ALL, WRITE); pub const ALL_EXEC: Mode = Mode::new().with_class_perm(ALL, EXEC); #[derive(Clone, Copy, PartialEq, Eq)] pub struct Mode { value: u32, } impl Default for Mode { fn default() -> Self { Self { value: 0 } } } impl fmt::Debug for Mode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_string()) } } impl From for Mode { fn from(value: u32) -> Self { Self { value } } } impl BitAnd for Mode { type Output = Self; fn bitand(self, other: Self) -> Self { Self { value: self.value & other.value, } } } impl BitAndAssign for Mode { fn bitand_assign(&mut self, other: Self) { self.value &= other.value; } } impl BitOr for Mode { type Output = Self; fn bitor(self, other: Self) -> Self { Self { value: self.value | other.value, } } } impl BitOrAssign for Mode { fn bitor_assign(&mut self, other: Self) { self.value |= other.value; } } impl Not for Mode { type Output = Self; fn not(self) -> Self::Output { Self { value: !self.value } } } impl Display for Mode { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_char(if self.has(USER_READ) { 'r' } else { '-' })?; f.write_char(if self.has(USER_WRITE) { 'w' } else { '-' })?; f.write_char(if self.has(USER_EXEC) { 'x' } else { '-' })?; f.write_char(if self.has(GROUP_READ) { 'r' } else { '-' })?; f.write_char(if self.has(GROUP_WRITE) { 'w' } else { '-' })?; f.write_char(if self.has(GROUP_EXEC) { 'x' } else { '-' })?; f.write_char(if self.has(OTHERS_READ) { 'r' } else { '-' })?; f.write_char(if self.has(OTHERS_WRITE) { 'w' } else { '-' })?; f.write_char(if self.has(OTHERS_EXEC) { 'x' } else { '-' })?; Ok(()) } } impl Mode { /// build a mode with absolutely no permission #[inline(always)] pub const fn new() -> Self { Self { value: 0 } } /// build a mode with all permissions given to everybody #[inline(always)] pub const fn all() -> Self { Self { value: 0b111111111 } } /// return the mode for the given path. /// On non unix platforms, return `Mode::all()` #[allow(unused_variables)] pub fn try_from(path: &Path) -> Result { #[cfg(unix)] { let metadata = fs::metadata(&path)?; Ok(Mode::from(metadata.mode())) } #[cfg(not(unix))] Ok(Self::all()) } /// finds if the mode indicates an executable file #[inline(always)] pub const fn is_exe(self) -> bool { (self.value & 0o111) != 0 } /// indicates whether the passed class/permissions are all present in self #[inline(always)] pub const fn has(self, other: Self) -> bool { self.value & other.value == other.value } /// return a new mode, with the permission added for the class /// (does nothing if the permission is already given to that class) #[inline(always)] pub const fn with_class_perm(self, class: Class, perm: Permission) -> Self { Self { value: self.value | (class & perm), } } /// return a new mode, with the permission removed for the class /// (does nothing if the permission is already given to that class) #[inline(always)] pub const fn without_class_perm(self, class: Class, perm: Permission) -> Self { Self { value: self.value & !(class & perm), } } /// add the class/permissions of the other mode #[inline(always)] pub const fn with(self, other: Mode) -> Self { Self { value: self.value | other.value, } } /// remove the class/permissions of the other mode #[inline(always)] pub const fn without(self, other: Mode) -> Self { Self { value: self.value & !other.value, } } } #[test] fn test_build() { let mut m = Mode::new() .with_class_perm(ALL, READ) .with_class_perm(USER, WRITE); assert_eq!("rw-r--r--", m.to_string()); m |= ALL_EXEC; assert_eq!("rwxr-xr-x", m.to_string()); m &= !USER_READ; assert_eq!("-wxr-xr-x", m.to_string()); } #[test] fn test_print() { assert_eq!("rw-r--r--", Mode::from(0b110100100).to_string()); assert_eq!("rw-r--r--", Mode::from(0o644).to_string()); let mu = Mode::from(0o600); let mo = Mode::from(0o004); assert_eq!("rw-------", mu.to_string()); assert_eq!("------r--", mo.to_string()); let muo = mu | mo; assert_eq!("rw----r--", muo.to_string()); }