Skip to main content

win_durable_ioring/windows_backend/
file_attributes.rs

1// Copyright (c) 2026 Mike Grier
2//! [`FileAttributes`]: the attributes a file is created with, as a typed set (DI-14.3, DI-D-64).
3//!
4//! `CreateFileW` takes the attributes of a file it creates in `dwFlagsAndAttributes`. The set here is
5//! the six the table keeps: `ARCHIVE`, `HIDDEN`, `OFFLINE`, `READONLY`, `SYSTEM` and `TEMPORARY`.
6//! `FILE_ATTRIBUTE_NORMAL` is not a member, since the page says it is valid only alone and that any
7//! other attribute overrides it, so it is the absence of attributes and the empty set stands for it.
8//! `FILE_ATTRIBUTE_ENCRYPTED` is excluded (DI-D-64). There is no constructor from a raw number, so the
9//! only masks that exist are subsets of the six.
10
11use std::fmt;
12use std::ops::{BitOr, BitOrAssign};
13
14use windows_sys::Win32::Storage::FileSystem::{
15    FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_READONLY,
16    FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY,
17};
18
19#[cfg(test)]
20mod tests;
21
22/// The attributes a file is created with: a set of `ARCHIVE`, `HIDDEN`, `OFFLINE`, `READONLY`, `SYSTEM`
23/// and `TEMPORARY`, named as `CreateFileW` names `FILE_ATTRIBUTE_*` without their prefix.
24///
25/// ```
26/// use win_durable_ioring::windows_backend::FileAttributes;
27///
28/// let attributes = FileAttributes::HIDDEN | FileAttributes::SYSTEM;
29///
30/// assert!(attributes.contains(FileAttributes::HIDDEN));
31/// assert!(!attributes.contains(FileAttributes::READONLY));
32/// assert!(FileAttributes::NONE.is_empty());
33/// ```
34///
35/// They apply to a file the open creates. The `CreateFileW` page says that for a file that exists the
36/// supplied attributes are generally combined with it or ignored, which is Win32's and is not
37/// refined here; [`FileOptions::attributes`](super::FileOptions::attributes) says what was measured.
38/// The page also says applications should not change `OFFLINE` arbitrarily.
39///
40/// The empty set asks for no attribute and is what an open passes when none is given; it is not
41/// `FILE_ATTRIBUTE_NORMAL`, which the page defines as valid only alone.
42#[derive(Clone, Copy, PartialEq, Eq, Hash)]
43pub struct FileAttributes(u32);
44
45/// Names the members once, so the constants, the names `Debug` prints and the tests that walk every
46/// subset are one list and not three that can disagree.
47macro_rules! attributes {
48    ( $( $(#[$doc:meta])* $name:ident = $value:expr; )* ) => {
49        impl FileAttributes {
50            $( $(#[$doc])* pub const $name: Self = Self($value); )*
51        }
52
53        /// Every named member, in the order they are declared.
54        const NAMED: &[(&str, FileAttributes)] = &[ $( (stringify!($name), FileAttributes::$name), )* ];
55    };
56}
57
58attributes! {
59    /// `FILE_ATTRIBUTE_ARCHIVE`: the file is marked for backup or removal.
60    ARCHIVE = FILE_ATTRIBUTE_ARCHIVE;
61    /// `FILE_ATTRIBUTE_HIDDEN`: the file is hidden, and not included in an ordinary directory listing.
62    HIDDEN = FILE_ATTRIBUTE_HIDDEN;
63    /// `FILE_ATTRIBUTE_OFFLINE`: the data is not immediately available, because it has been moved to
64    /// offline storage. The `CreateFileW` page says an application should not change it arbitrarily.
65    OFFLINE = FILE_ATTRIBUTE_OFFLINE;
66    /// `FILE_ATTRIBUTE_READONLY`: the file can be read and not written or deleted.
67    READONLY = FILE_ATTRIBUTE_READONLY;
68    /// `FILE_ATTRIBUTE_SYSTEM`: the file is part of, or used exclusively by, the operating system.
69    SYSTEM = FILE_ATTRIBUTE_SYSTEM;
70    /// `FILE_ATTRIBUTE_TEMPORARY`: the file is for temporary storage. The page says it lets a file
71    /// system keep the data in the cache and avoid writing it back while memory allows.
72    TEMPORARY = FILE_ATTRIBUTE_TEMPORARY;
73}
74
75impl FileAttributes {
76    /// No attribute: what an open asks for unless it is told otherwise.
77    pub const NONE: Self = Self(0);
78
79    /// The union of two sets.
80    #[must_use]
81    pub const fn union(self, other: Self) -> Self {
82        Self(self.0 | other.0)
83    }
84
85    /// Whether every member of `other` is in this set.
86    #[must_use]
87    pub const fn contains(self, other: Self) -> bool {
88        self.0 & other.0 == other.0
89    }
90
91    /// Whether the set has no member.
92    #[must_use]
93    pub const fn is_empty(self) -> bool {
94        self.0 == 0
95    }
96
97    /// The mask this set is in `dwFlagsAndAttributes`.
98    pub(super) const fn bits(self) -> u32 {
99        self.0
100    }
101}
102
103impl BitOr for FileAttributes {
104    type Output = Self;
105
106    fn bitor(self, other: Self) -> Self {
107        self.union(other)
108    }
109}
110
111impl BitOrAssign for FileAttributes {
112    fn bitor_assign(&mut self, other: Self) {
113        *self = self.union(other);
114    }
115}
116
117impl fmt::Debug for FileAttributes {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str("FileAttributes(")?;
120        if self.is_empty() {
121            f.write_str("none")?;
122        } else {
123            let mut first = true;
124            for (name, _) in NAMED.iter().filter(|(_, member)| self.contains(*member)) {
125                if !first {
126                    f.write_str(" | ")?;
127                }
128                first = false;
129                f.write_str(name)?;
130            }
131        }
132        f.write_str(")")
133    }
134}