readable_perms/
lib.rs

1//! Unix permissions `mode_t` in readable format.
2//!
3//No I don't care about Microshaft wangblows
4#![cfg_attr(nightly, feature(test))]
5#![allow(non_upper_case_globals)]
6#![allow(dead_code)]
7
8use bitflags::bitflags;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11enum Class
12{
13    Owner(Bit),
14    Group(Bit),
15    Other(Bit),
16}
17
18impl Class
19{
20    /// Lmfao. Fuck bit masks and unix permissiong, `const fn` so idc
21    #[cfg(nightly)]
22    const fn mode(&self) -> u32
23    {
24	macro_rules! map {
25	    ($bit:expr, $bt:path, $constant:ident) => {
26		if $bit.contains($bt) {
27		    $constant
28		} else {
29		    0
30		}
31	    }
32	}
33	match self {
34	    Self::Owner(bit) => {
35		0u32 |
36		map!(bit, Bit::Read, MODE_USER_READ) |
37		map!(bit, Bit::Write, MODE_USER_WRITE) |
38		map!(bit, Bit::Execute, MODE_USER_EXECUTE)
39	    },
40	    Self::Group(bit) => {
41		0u32 |
42		map!(bit, Bit::Read, MODE_GROUP_READ) |
43		map!(bit, Bit::Write, MODE_GROUP_WRITE) |
44		map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
45	    },
46	    Self::Other(bit) => {
47		0u32 |
48		map!(bit, Bit::Read, MODE_OTHER_READ) |
49		map!(bit, Bit::Write, MODE_OTHER_WRITE) |
50		map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
51	    },
52	}
53    }
54    #[cfg(not(nightly))]
55    fn mode(&self) -> u32 //rip stable
56    {
57	macro_rules! map {
58	    ($bit:expr, $bt:path, $constant:ident) => {
59		if $bit.contains($bt) {
60		    $constant
61		} else {
62		    0
63		}
64	    }
65	}
66	match self {
67	    Self::Owner(bit) => {
68		0u32 |
69		map!(bit, Bit::Read, MODE_USER_READ) |
70		map!(bit, Bit::Write, MODE_USER_WRITE) |
71		map!(bit, Bit::Execute, MODE_USER_EXECUTE)
72	    },
73	    Self::Group(bit) => {
74		0u32 |
75		map!(bit, Bit::Read, MODE_GROUP_READ) |
76		map!(bit, Bit::Write, MODE_GROUP_WRITE) |
77		map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
78	    },
79	    Self::Other(bit) => {
80		0u32 |
81		map!(bit, Bit::Read, MODE_OTHER_READ) |
82		map!(bit, Bit::Write, MODE_OTHER_WRITE) |
83		map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
84	    },
85	}
86    }
87
88    #[cfg(nightly)] const fn mask_mode(&self, bit: u32) -> Bit
89    {
90	macro_rules! map {
91	    ($bit:expr, $bt:path, $constant:ident) => {
92		if ($bit & $constant) == $constant {$bt.bits()} else {0u32}
93	    }
94	}
95	
96	Bit::from_bits_truncate(match self {
97	    Self::Owner(_) => {
98		map!(bit, Bit::Read, MODE_USER_READ) |
99		map!(bit, Bit::Write, MODE_USER_WRITE) |
100		map!(bit, Bit::Execute, MODE_USER_EXECUTE)
101	    },
102	    Self::Group(_) => {
103		map!(bit, Bit::Read, MODE_GROUP_READ) |
104		map!(bit, Bit::Write, MODE_GROUP_WRITE) |
105		map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
106	    },
107	    Self::Other(_) => {
108		map!(bit, Bit::Read, MODE_OTHER_READ) |
109		map!(bit, Bit::Write, MODE_OTHER_WRITE) |
110		map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
111	    },
112	})
113    }
114    #[cfg(not(nightly))] fn mask_mode(&self, bit: u32) -> Bit
115    {
116	macro_rules! map {
117	    ($bit:expr, $bt:path, $constant:ident) => {
118		if ($bit & $constant) == $constant {$bt.bits()} else {0u32}
119	    }
120	}
121	
122	Bit::from_bits_truncate(match self {
123	    Self::Owner(_) => {
124		map!(bit, Bit::Read, MODE_USER_READ) |
125		map!(bit, Bit::Write, MODE_USER_WRITE) |
126		map!(bit, Bit::Execute, MODE_USER_EXECUTE)
127	    },
128	    Self::Group(_) => {
129		map!(bit, Bit::Read, MODE_GROUP_READ) |
130		map!(bit, Bit::Write, MODE_GROUP_WRITE) |
131		map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
132	    },
133	    Self::Other(_) => {
134		map!(bit, Bit::Read, MODE_OTHER_READ) |
135		map!(bit, Bit::Write, MODE_OTHER_WRITE) |
136		map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
137	    },
138	})
139    }
140}
141
142const MODE_USER: u32 = 0o700;
143const MODE_USER_READ: u32 = 0o400;
144const MODE_USER_WRITE: u32 = 0o200;
145const MODE_USER_EXECUTE: u32 = 0o100;
146
147const MODE_GROUP: u32 = 0o70;
148const MODE_GROUP_READ: u32 = 0o40;
149const MODE_GROUP_WRITE: u32 = 0o20;
150const MODE_GROUP_EXECUTE: u32 = 0o10;
151
152const MODE_OTHER: u32 = 0o7;
153const MODE_OTHER_READ: u32 = 0o4;
154const MODE_OTHER_WRITE: u32 = 0o2;
155const MODE_OTHER_EXECUTE: u32 = 0o1;
156
157const MODE: u32 = 0o777;
158
159bitflags!{
160    /// The mode mask for each individual class of `User`.
161    ///
162    /// # Notes
163    /// No this does not map directly to `mode_t`. Use `User.mode(bits)` and `User.from_mode(mode)`.
164    pub struct Bit: u32 {
165	/// No permissions for this class
166	const None = 0;
167	/// Read permissions for this class
168	const Read = 1;
169	/// Write permissions for this class
170	const Write = 2;
171	/// Execute permissions for this class
172	///
173	/// # Notes
174	/// For directories, this translates to `can chdir to here'
175	const Execute = 4;
176	
177	/// Read + Write + Execute. The whole mask.
178	const Mask = 7;
179    }
180}
181
182/*bitflags!{
183// not supported, don't care
184struct StickyBit: u32{
185const Empty = 0;
186const Setuid = 0o40000;
187const Setgid = 0o20000;
188const SaveSwap = 0o10000;
189    }
190}*/
191
192/// Permissions struct in readable format
193#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
194#[repr(C)]
195pub struct Permissions
196{
197    /// The `S_IRWXU` mask.
198    ///
199    /// # In English
200    /// Read, write, and execute mask for owner of the file
201    pub owner: Bit,
202    
203    /// The `S_IRWXG` mask.
204    ///
205    /// # In English
206    /// Read, write, and execute mask for members of the owner's group
207    pub group: Bit,
208    
209    /// The `S_IRWXO` mask.
210    ///
211    /// # In English
212    /// Read, write, and execute mask for anyone else
213    pub other: Bit,
214    
215    u_mask: u32, 
216}
217
218/// The class of user that UNIX permissions care about
219#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
220pub enum User
221{
222    /// Owner of the file directly
223    Owner,
224    /// Members of the owner's group
225    Group,
226    /// Anyony else
227    Other,
228}
229
230impl User
231{
232    /// Convert this class's representation of `Bit` into the corresponding `mode_t`.
233    #[cfg(nightly)] pub const fn mode(self, mask: Bit) -> u32
234    {
235	match self {
236	    User::Owner => Class::Owner(mask).mode(),
237	    User::Group => Class::Group(mask).mode(),
238	    User::Other => Class::Other(mask).mode(),
239	}
240    }
241    /// Convert this class's representation of `Bit` into the corresponding `mode_t`.
242    #[cfg(not(nightly))] pub fn mode(self, mask: Bit) -> u32
243    {
244	match self {
245	    User::Owner => Class::Owner(mask).mode(),
246	    User::Group => Class::Group(mask).mode(),
247	    User::Other => Class::Other(mask).mode(),
248	}
249    }
250    
251
252    /// Convert a `mode_t` `u32` into `Bit` with this class' user.
253    #[cfg(nightly)] pub const fn from_mode(self, mask: u32) -> Bit
254    {
255	match self {
256	    User::Owner => Class::Owner(Bit::Mask).mask_mode(mask),
257	    User::Group => Class::Group(Bit::Mask).mask_mode(mask),
258	    User::Other => Class::Other(Bit::Mask).mask_mode(mask),
259	}
260    }
261    
262    /// Convert a `mode_t` `u32` into `Bit` with this class' user.
263    #[cfg(not(nightly))] pub fn from_mode(self, mask: u32) -> Bit
264    {
265	match self {
266	    User::Owner => Class::Owner(Bit::Mask).mask_mode(mask),
267	    User::Group => Class::Group(Bit::Mask).mask_mode(mask),
268	    User::Other => Class::Other(Bit::Mask).mask_mode(mask),
269	}
270    }
271}
272
273impl Default for Permissions
274{
275    #[inline] fn default() -> Self
276    {
277	Self::new()
278    }
279}
280
281impl Permissions
282{
283    /// Create a new empty `Permissions` struct
284    pub const fn new() -> Self
285    {
286	Self {
287	    owner: Bit::None,
288	    group: Bit::None,
289	    other: Bit::None,
290
291	    u_mask: 0u32,
292	}
293    }
294}
295
296// Mask impl
297impl Permissions
298{
299    #[inline] const fn c_owner(&self) -> Class
300    {
301	Class::Owner(self.owner)
302    }
303    #[inline] const fn c_group(&self) -> Class
304    {
305	Class::Group(self.group)
306    }
307    #[inline] const fn c_other(&self) -> Class
308    {
309	Class::Other(self.other)
310    }
311
312    /// Convert into `mode_t` representation.
313    #[inline] #[cfg(nightly)]  const fn mask_calc(&self) -> u32
314    {
315	self.c_owner().mode() |
316	self.c_group().mode() |
317	self.c_other().mode()
318    }
319
320    #[inline] #[cfg(nightly)] const fn into_calced(mut self) -> Self
321    {
322	self.u_mask = self.mask_calc();
323	self
324    }
325    
326    /// Convert into `mode_t` representation.
327    #[inline] pub const fn mask(&self) -> u32
328    {
329	self.u_mask
330    }
331    
332    /// Convert into `mode_t` representation.
333    #[inline] #[cfg(not(nightly))] fn mask_calc(&self) -> u32
334    {
335	self.c_owner().mode() |
336	self.c_group().mode() |
337	self.c_other().mode()
338    }
339    
340    /// Convert from `mode_t` representation (slow).
341    #[inline] #[cfg(nightly)]  const fn from_mask_calc(bit: u32) -> Self
342    {
343	Self{
344	    owner: Class::Owner(Bit::Mask).mask_mode(bit),
345	    group: Class::Group(Bit::Mask).mask_mode(bit),
346	    other: Class::Other(Bit::Mask).mask_mode(bit),
347
348	    u_mask: 0,
349	}.into_calced()
350    }
351    
352    /// Convert from `mode_t` representation.
353    #[inline] pub const fn from_mask(from: u32) -> Self
354    {
355	MAP[(from & MODE) as usize]
356    }
357
358    /// Consume and add a mask.
359    ///
360    /// # Usage
361    /// For builder pattern
362    #[inline] #[cfg(nightly)] pub const fn add_mask(self, class: User, bit: Bit) -> Self
363    {
364	Self {
365	    u_mask: self.u_mask | class.mode(bit),
366	    ..match class {
367		User::Owner => {
368		    Self {
369			owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
370			..self
371		    }
372		},
373		User::Group => {
374		    Self {
375			group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
376			..self
377		    }
378		},
379		User::Other => {
380		    Self {
381			other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
382			..self
383		    }
384		},
385	    }
386	}
387    }
388    
389    /// Consume and add a mask.
390    ///
391    /// # Usage
392    /// For builder pattern
393    #[inline] #[cfg(not(nightly))] pub fn add_mask(self, class: User, bit: Bit) -> Self
394    {
395	Self {
396	    u_mask: self.u_mask | class.mode(bit),
397	    ..match class {
398		User::Owner => {
399		    Self {
400			owner: Bit::from_bits_truncate(self.owner.bits() | bit.bits()),
401			..self
402		    }
403		},
404		User::Group => {
405		    Self {
406			group: Bit::from_bits_truncate(self.group.bits() | bit.bits()),
407
408			..self
409		    }
410		},
411		User::Other => {
412		    Self {
413			other: Bit::from_bits_truncate(self.other.bits() | bit.bits()),
414
415			..self
416		    }
417		},
418	    }
419	}
420    }
421
422    
423    /// Consume and remove a mask.
424    ///
425    /// # Usage
426    /// For builder pattern
427    #[inline] #[cfg(nightly)] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
428    {
429	Self{
430	    u_mask: self.u_mask & !class.mode(bit),
431	    ..match class {
432		User::Owner => {
433		    Self {
434			owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
435			..self
436		    }
437		},
438		User::Group => {
439		    Self {
440			group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
441			..self
442		    }
443		},
444		User::Other => {
445		    Self {
446			other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
447			..self
448		    }
449		},
450	    }
451	}
452    }
453
454    
455    /// Consume and remove a mask.
456    ///
457    /// # Usage
458    /// For builder pattern
459    #[inline] #[cfg(not(nightly))] pub fn remove_mask(self, class: User, bit: Bit) -> Self
460    {
461	Self {
462	    u_mask: self.u_mask & !class.mode(bit),
463	    ..match class {
464		User::Owner => {
465		    Self {
466			owner: Bit::from_bits_truncate(self.owner.bits() & !bit.bits()),
467			..self
468		    }
469		},
470		User::Group => {
471		    Self {
472			group: Bit::from_bits_truncate(self.group.bits() & !bit.bits()),
473			..self
474		    }
475		},
476		User::Other => {
477		    Self {
478			other: Bit::from_bits_truncate(self.other.bits() & !bit.bits()),
479			..self
480		    }
481		},
482	    }
483	}
484    }
485
486    /// Returns true if the specified mode mask is all present
487    #[inline] #[cfg(nightly)]  pub const fn has_mask(&self, class: User, bit: Bit) -> bool
488    {
489	match class {
490	    User::Owner => {
491		(self.owner.bits() & bit.bits()) == bit.bits()
492	    },
493	    User::Group => {
494		(self.group.bits() & bit.bits()) == bit.bits()
495	    },
496	    User::Other => {
497		(self.other.bits() & bit.bits()) == bit.bits()
498	    },
499	}
500    }
501
502    /// Returns true if the specified mode mask is all present
503    #[inline] #[cfg(not(nightly))] pub fn has_mask(&self, class: User, bit: Bit) -> bool
504    {
505	match class {
506	    User::Owner => {
507		(self.owner.bits() & bit.bits()) == bit.bits()
508	    },
509	    User::Group => {
510		(self.group.bits() & bit.bits()) == bit.bits()
511	    },
512	    User::Other => {
513		(self.other.bits() & bit.bits()) == bit.bits()
514	    },
515	}
516    }
517}
518
519impl From<Permissions> for u32
520{
521    #[inline] fn from(from: Permissions) -> Self
522    {
523	debug_assert_eq!(from.mask_calc(), from.mask());
524	from.mask()
525    }
526}
527
528impl From<u32> for Permissions
529{
530    #[inline] fn from(from: u32) -> Self
531    {
532	Self::from_mask(from)
533    }
534}
535
536mod test;
537#[cfg(nightly)] mod bench;
538
539const fn generate_struct() -> [Permissions; 512]
540{
541    #[cfg(nightly)] return {
542	let mut i: u32 =0;
543	let mut output = [Permissions::new(); 512];
544	loop
545	{
546	    output[i as usize] = Permissions::from_mask_calc(i);
547	    i+=1;
548	    if i == 0o777 { //ye idk
549		output[0o777] = Permissions::new()
550		    .add_mask(User::Owner, Bit::Mask)
551		    .add_mask(User::Group, Bit::Mask)
552		    .add_mask(User::Other, Bit::Mask);
553		break;
554	    }
555	}
556	output
557    };
558
559    #[cfg(not(nightly))] {
560	stable::MAP
561    }
562}
563
564
565#[cfg(feature="speedup_hack_stable")] 
566mod output {
567    use super::*;
568    use std::{
569	io::{
570	    self,
571	    Write,
572	},
573    };
574    pub fn print_map_as_syntax_hack<W: Write+?Sized>(to: &mut W) -> io::Result<()>
575    {
576	fn print_bit<W: Write+?Sized>(to:&mut W, bit: &Bit) -> io::Result<()>
577	{
578	    macro_rules! dobit {
579		($bit:ident, $p:path) => {
580		    if $bit.contains($p) {
581			write!(to, "| {}", $p.bits())?;
582			
583		    }
584		}
585	    }
586	    write!(to, "Bit::from_bits_truncate(0u32 ")?;
587	    dobit!(bit, Bit::Read);
588	    dobit!(bit, Bit::Write);
589	    dobit!(bit, Bit::Execute);
590	    write!(to, "| 0)")?;
591	    Ok(())
592	}
593
594	writeln!(to, "[")?;
595	for perm in MAP.iter() {
596	    write!(to, "Permissions {{owner: ")?;
597	    print_bit(to,&perm.owner)?;
598	    write!(to, ", group: ")?;
599	    print_bit(to,&perm.group)?;
600	    write!(to, ", other: ")?;
601	    print_bit(to,&perm.other)?;
602	    write!(to, ", u_mask: {}}}, ", perm.mask_calc())?;
603	}
604	writeln!(to, "]")?;
605	Ok(())
606    }
607}
608
609#[cfg(feature="speedup_hack_stable")]
610fn print_map_as_syntax_hack()
611{
612    let stdout = std::io::stdout();
613    let mut stdout = stdout.lock();
614    output::print_map_as_syntax_hack(&mut stdout).unwrap();
615}
616
617/// Generates the build map for stable release.
618#[cfg(feature="speedup_hack_stable")]
619pub fn generate_build_map() {
620    use std::fs::OpenOptions;
621    use std::io::Write;
622    
623    let mut file = OpenOptions::new()
624	.create(true)
625	.truncate(true)
626	.write(true)
627	.open("./src/stable/mod.rs").expect("Failed to open file");
628
629    writeln!(&mut file, "//! Hack for fast in stable with no `const fn` stuffs\n").unwrap();
630    writeln!(&mut file, "use super::*;").unwrap();
631
632    writeln!(&mut file, "pub const MAP: [Permissions; 512] = ").unwrap();
633    output::print_map_as_syntax_hack(&mut file).expect("Failed to write");
634    writeln!(&mut file, ";").unwrap();
635}
636
637#[cfg(not(nightly))] mod stable;
638
639const MAP: [Permissions; 512] = generate_struct();
640
641#[cfg(target_family="unix")]
642mod ext;
643#[cfg(target_family="unix")]
644pub use ext::*;
645
646// Boilerplate
647use std::{
648    borrow::Borrow,
649    cmp::{PartialEq,Eq},
650};
651
652impl AsRef<Permissions> for u32
653{
654    fn as_ref(&self) -> &Permissions
655    {
656	&MAP[(*self & 0o777u32) as usize]
657    }
658}
659
660impl AsRef<Permissions> for Permissions
661{
662    fn as_ref(&self) -> &Permissions
663    {
664	&self
665    }
666}
667
668impl Borrow<Permissions> for u32
669{
670    fn borrow(&self) -> &Permissions
671    {
672	self.as_ref()
673    }
674}
675
676impl PartialEq<u32> for Permissions
677{
678    fn eq(&self, other: &u32) -> bool
679    {
680	&Self::from(*other) == self
681    }
682}
683
684impl PartialEq<Permissions> for u32
685{
686    fn eq(&self, other: &Permissions) -> bool
687    {
688	other.eq(self)
689    }
690}
691
692impl std::fmt::Display for Permissions
693{
694    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
695    {
696	write!(f, "0{}", self.mask())
697    }
698}