1#![no_std]
2#![forbid(unsafe_code)]
3
4extern crate core;
5
6use bitflags::bitflags;
7use core::fmt::{self, Write};
8
9#[cfg(feature = "num")]
10use num_derive as nd;
11
12#[repr(u16)]
13#[rustfmt::skip]
14#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[cfg_attr(feature = "num", derive(nd::FromPrimitive, nd::ToPrimitive))]
16#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
17pub enum FileType {
18 IFDIR = 0o040000,
20 IFCHR = 0o020000,
22 IFBLK = 0o060000,
24 IFREG = 0o100000,
26 IFIFO = 0o010000,
28 IFLNK = 0o120000,
30 IFSOCK = 0o140000,
32}
33
34impl FileType {
35 pub const IFMT: u16 = 0o170000;
37
38 #[inline(always)]
40 #[cfg(feature = "num")]
41 pub fn from_bits(x: u16) -> Option<Self> {
42 <Self as num_traits::FromPrimitive>::from_u16(x)
43 }
44
45 #[inline(always)]
47 pub fn bits(&self) -> u16 {
48 *self as _
49 }
50}
51
52impl fmt::Display for FileType {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 f.write_char(match self {
55 Self::IFSOCK => 's',
56 Self::IFLNK => 'l',
57 Self::IFREG => '-',
58 Self::IFBLK => 'b',
59 Self::IFDIR => 'd',
60 Self::IFCHR => 'c',
61 Self::IFIFO => 'p',
62 })
63 }
64}
65
66bitflags! {
67 #[derive(Default)]
68 #[repr(transparent)]
69 #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
70 #[cfg_attr(feature = "serde", serde(transparent))]
71 pub struct Mode: u16 {
72 const ISUID = 0o004000;
75 const ISGID = 0o002000;
77 const ISVTX = 0o001000;
79
80 const IREAD = 0o000400;
83 const IWRITE = 0o000200;
85 const IEXEC = 0o000100;
87
88 const IRUSR = Self::IREAD.bits;
91 const IWUSR = Self::IWRITE.bits;
93 const IXUSR = Self::IEXEC.bits;
95 const IRWXU = Self::IRUSR.bits | Self::IWUSR.bits | Self::IXUSR.bits;
96
97 const IRGRP = Self::IRUSR.bits >> 3;
100 const IWGRP = Self::IWUSR.bits >> 3;
102 const IXGRP = Self::IXUSR.bits >> 3;
104 const IRWXG = Self::IRGRP.bits | Self::IWGRP.bits | Self::IXGRP.bits;
105
106 const IROTH = Self::IRGRP.bits >> 3;
109 const IWOTH = Self::IWGRP.bits >> 3;
111 const IXOTH = Self::IXGRP.bits >> 3;
113 const IRWXO = Self::IROTH.bits | Self::IWOTH.bits | Self::IXOTH.bits;
114 }
115}
116
117impl Mode {
118 fn fmt_rwx_bits(&self, shift: u8, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let sxbit = match shift {
120 2 => self.contains(Self::ISUID),
121 1 => self.contains(Self::ISGID),
122 0 => self.contains(Self::ISVTX),
123 _ => panic!("fmt_rwx_bits: illegal shift value"),
124 };
125 let protbits = (self.bits >> (3 * shift)) & 0o7;
126 f.write_char(if (protbits & 0o4) > 0 { 'r' } else { '-' })?;
127 f.write_char(if (protbits & 0o2) > 0 { 'w' } else { '-' })?;
128 f.write_char(match (shift, (protbits & 0o1) > 0, sxbit) {
129 (0, true, true) => 't',
130 (0, false, true) => 'T',
131 (_, true, true) => 's',
132 (_, false, true) => 'S',
133 (_, true, false) => 'x',
134 (_, false, false) => '-',
135 })?;
136 Ok(())
137 }
138}
139
140impl fmt::Display for Mode {
141 #[inline]
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 self.fmt_rwx_bits(2, f)?;
144 self.fmt_rwx_bits(1, f)?;
145 self.fmt_rwx_bits(0, f)?;
146 Ok(())
147 }
148}
149
150#[cfg(feature = "num-traits")]
151impl num_traits::FromPrimitive for Mode {
152 #[inline]
153 fn from_i64(n: i64) -> Option<Self> {
154 Self::from_u16(u16::from_i64(n)?)
155 }
156
157 #[inline]
158 fn from_u64(n: u64) -> Option<Self> {
159 Self::from_u16(u16::from_u64(n)?)
160 }
161
162 #[inline(always)]
163 fn from_u16(n: u16) -> Option<Self> {
164 Self::from_bits(n)
165 }
166}
167
168#[cfg(feature = "num-traits")]
169impl num_traits::ToPrimitive for Mode {
170 #[inline(always)]
171 fn to_u64(&self) -> Option<u64> {
172 Some(self.bits.into())
173 }
174 #[inline(always)]
175 fn to_u16(&self) -> Option<u16> {
176 Some(self.bits)
177 }
178 #[inline(always)]
179 fn to_i64(&self) -> Option<i64> {
180 Some(self.bits.into())
181 }
182}
183
184#[cfg(feature = "num")]
186pub fn split(fmode: u16) -> Option<(FileType, Mode)> {
187 let ft = FileType::from_bits(fmode & FileType::IFMT)?;
188 let md = Mode::from_bits(fmode & !FileType::IFMT)?;
189 Some((ft, md))
190}
191
192#[cfg(all(unix, any(test, feature = "nix")))]
193mod nix_;
194
195#[cfg(feature = "umask")]
220impl From<Mode> for umask::Mode {
221 #[inline]
222 fn from(x: Mode) -> umask::Mode {
223 umask::Mode::from(u32::from(x.bits))
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 #[test]
230 fn display() {
231 macro_rules! bytes_and_str {
232 ($($a:expr => $b:expr),+ $(,)?) => {
233 $( assert_eq!(std::format!("{}", crate::Mode::from_bits($a).expect("unknown bitmask")), $b); )+
234 }
235 };
236 extern crate std;
237 bytes_and_str! {
238 0o0200 => "-w-------",
239 0o0706 => "rwx---rw-",
240 0o0074 => "---rwxr--",
241 0o0777 => "rwxrwxrwx",
242 0o1777 => "rwxrwxrwt",
243 0o2777 => "rwxrwsrwx",
244 0o4777 => "rwsrwxrwx",
245 0o7777 => "rwsrwsrwt",
246 0o7747 => "rwsr-Srwt",
247 0o7000 => "--S--S--T",
248 }
249 }
250}