Skip to main content

mountpoint_s3_fs/fs/
flags.rs

1/// Helper to define type-safe flags from `libc` constants.
2/// Usage:
3///
4/// ```ignore
5/// struct MyFlags(u32);
6///
7/// libc_flags! {
8///     MyFlags : u32 {
9///         A,
10///         B,
11///         C,
12///     }
13/// }
14/// ```
15///
16/// See [`OpenFlags`] for an example.
17macro_rules! libc_flags {
18    (
19        $struct_name:ident : $raw_type:ty {
20        $(
21            $(#[$flag_attr:ident $($flag_meta_args:tt)*])*
22            $flag_name:ident
23        ),*$(,)+
24    }
25    ) => {
26        bitflags::bitflags! {
27            impl $struct_name : $raw_type {
28                $(
29                    $(#[$flag_attr $($flag_meta_args)*])*
30                    const $flag_name = libc::$flag_name;
31                )*
32
33                const _ = !0;
34            }
35        }
36
37        impl std::fmt::Display for $struct_name {
38            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39                bitflags::parser::to_writer(self, f)
40            }
41        }
42
43        impl std::fmt::Debug for $struct_name {
44            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45                write!(f, "{}({:#x} = {})", stringify!($struct_name), self.0, self.to_string())
46            }
47        }
48
49        impl From<$raw_type> for $struct_name {
50            fn from(value: $raw_type) -> Self {
51                Self::from_bits_retain(value)
52            }
53        }
54    }
55}
56
57/// Flags used in [`open`](super::S3Filesystem::open).
58///
59/// Note that the "access mode" constants are not mapped as values because they do not behave
60/// like normal bit flags (and will not be shown in `Debug`/`Display`):
61/// * `libc::O_ACCMODE = 0b11` is a mask for the access mode,
62/// * `libc::O_RDONLY = 0b00` is also the default value.
63///
64/// The other access mode constants, `libc::O_WRONLY = 0b01` and `libc::O_RDWR = 0b10`, are
65/// valid bit flags and mapped respectively to `OpenFlags::O_WRONLY` and `OpenFlags::O_RDWR`.
66#[derive(Clone, Copy, PartialEq, Eq)]
67pub struct OpenFlags(i32);
68
69libc_flags! {
70    OpenFlags : i32 {
71        O_WRONLY,
72        O_RDWR,
73        O_APPEND,
74        O_SYNC,
75        O_TRUNC,
76        O_DSYNC,
77        O_NONBLOCK,
78        O_NOCTTY,
79        O_CREAT,
80
81        #[cfg(target_os = "linux")]
82        O_DIRECT,
83
84        // Incomplete list. To be integrated if/when required.
85    }
86}
87
88impl OpenFlags {
89    #[cfg(not(target_os = "linux"))]
90    pub fn direct_io(&self) -> bool {
91        false
92    }
93
94    #[cfg(target_os = "linux")]
95    pub fn direct_io(&self) -> bool {
96        self.contains(OpenFlags::O_DIRECT)
97    }
98}
99
100/// Flags used in [rename](super::S3Filesystem::rename).
101#[derive(Clone, Copy, PartialEq, Eq)]
102pub struct RenameFlags(u32);
103
104libc_flags! {
105    RenameFlags : u32 {
106        #[cfg(target_os = "linux")]
107        RENAME_NOREPLACE,
108        #[cfg(target_os = "linux")]
109        RENAME_EXCHANGE,
110        #[cfg(target_os = "linux")]
111        RENAME_WHITEOUT,
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn conversion_test() {
121        let raw = libc::O_WRONLY | libc::O_APPEND;
122        let flags: OpenFlags = raw.into();
123        assert_eq!(flags, OpenFlags::O_WRONLY | OpenFlags::O_APPEND);
124    }
125
126    #[test]
127    fn unknown_test() {
128        let raw = libc::O_ACCMODE; // Not defined as a value in `OpenFlags`.
129        let flags: OpenFlags = raw.into();
130        assert_eq!(flags.bits(), raw, "Unknown value should be preserved");
131    }
132
133    #[test]
134    fn debug_test() {
135        let flags = OpenFlags::O_WRONLY | OpenFlags::O_APPEND;
136        let expected = format!(
137            "OpenFlags({:#x} = O_WRONLY | O_APPEND)",
138            libc::O_WRONLY | libc::O_APPEND
139        );
140        assert_eq!(format!("{flags:?}"), expected);
141    }
142}