fuser_async/
utils.rs

1//! Utilities
2pub use outof::OutOf;
3
4use std::os::unix::fs::MetadataExt;
5
6use crate::error::Error;
7
8pub const BLOCK_SIZE: u32 = 512;
9impl TryFrom<&std::fs::DirEntry> for crate::DirEntry {
10    type Error = Error;
11    fn try_from(e: &std::fs::DirEntry) -> Result<Self, Error> {
12        let metadata = e.metadata().map_err(|_| Error::NoFileDir)?;
13        let name = e.file_name();
14        let name = name.to_str().ok_or(Error::Encoding)?;
15        Ok(Self {
16            inode: metadata.ino(),
17            name: name.into(),
18            file_type: file_type(&metadata)?,
19        })
20    }
21}
22
23/// Try getting the [`fuser::FileType`] from a [`std::fs::Metadata`].
24///
25/// For now, this only supports directories, files, and symlinks.
26pub fn file_type(metadata: &std::fs::Metadata) -> Result<fuser::FileType, Error> {
27    let ft = metadata.file_type();
28    if ft.is_dir() {
29        Ok(fuser::FileType::Directory)
30    } else if ft.is_file() {
31        Ok(fuser::FileType::RegularFile)
32    } else if ft.is_symlink() {
33        Ok(fuser::FileType::Symlink)
34    } else {
35        Err(Error::Unimplemented)
36    }
37}
38
39/// Crate basic [`fuser::FileAttr`] from inode, size and time.
40pub fn file_attr(ino: u64, size: u64, time: std::time::SystemTime) -> fuser::FileAttr {
41    fuser::FileAttr {
42        ino,
43        size,
44        blocks: (size as f64 / BLOCK_SIZE as f64).ceil() as u64,
45        atime: time,
46        mtime: time,
47        ctime: time,
48        crtime: time,
49        kind: fuser::FileType::RegularFile,
50        perm: 0o644,
51        nlink: 1,
52        uid: 501,
53        gid: 20,
54        rdev: 0,
55        flags: 0,
56        blksize: BLOCK_SIZE,
57    }
58}
59pub fn dir_attr(ino: u64, children: u32, time: std::time::SystemTime) -> fuser::FileAttr {
60    fuser::FileAttr {
61        ino,
62        size: 0,
63        blocks: 0,
64        atime: time,
65        mtime: time,
66        ctime: time,
67        crtime: time,
68        kind: fuser::FileType::Directory,
69        perm: 0o644,
70        nlink: children + 2,
71        uid: 501,
72        gid: 20,
73        rdev: 0,
74        flags: 0,
75        blksize: BLOCK_SIZE,
76    }
77}
78pub fn unix_timestamp() -> u64 {
79    std::time::SystemTime::now()
80        .duration_since(std::time::SystemTime::UNIX_EPOCH)
81        .unwrap()
82        .as_secs()
83}
84
85mod outof {
86    use num_traits::cast::AsPrimitive;
87    use serde::{Deserialize, Serialize};
88
89    static PERCENT_DEFAULT_DISPLAY_PRECISION: usize = 0;
90    /// Compute and display fractions.
91    ///
92    /// ```
93    /// use fuser_async::utils::OutOf;
94    /// let r = OutOf::new(1, 3);
95    /// assert_eq!(format!("{:.1}", r), "33.3%");
96    /// assert_eq!(format!("{:.0}", r), "33%");
97    /// ```
98    #[derive(Copy, Serialize, Deserialize, Clone)]
99    pub struct OutOf {
100        pub part: f64,
101        pub total: f64,
102    }
103    impl Default for OutOf {
104        fn default() -> Self {
105            Self {
106                part: 0.0,
107                total: 0.0,
108            }
109        }
110    }
111    impl AsPrimitive<f64> for OutOf {
112        fn as_(self) -> f64 {
113            self.part / self.total
114        }
115    }
116    impl OutOf {
117        pub fn new<A: AsPrimitive<f64>, B: AsPrimitive<f64>>(part: A, total: B) -> Self {
118            Self {
119                part: part.as_(),
120                total: total.as_(),
121            }
122        }
123        pub fn perc(&self) -> f64 {
124            self.part / self.total * 100.0
125        }
126        pub fn display_full(&self) -> String {
127            if self.total == 0.0 {
128                format!("{}/{}", self.part, self.total)
129            } else {
130                format!("{}/{} ({})", self.part, self.total, self)
131            }
132        }
133    }
134    impl std::fmt::Display for OutOf {
135        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
136            let prec = f.precision().unwrap_or(PERCENT_DEFAULT_DISPLAY_PRECISION);
137            let ratio = self.as_();
138            if ratio.is_finite() {
139                write!(f, "{:.*}%", prec, 100.0 * ratio)
140            } else {
141                Ok(())
142            }
143        }
144    }
145}