standard_lib/fs/file/
options.rs

1use std::fmt;
2use std::fmt::{Debug, Formatter};
3use std::marker::PhantomData;
4
5use libc::{O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_NOATIME, O_NOFOLLOW, O_SYNC, O_TMPFILE, O_TRUNC, c_int};
6
7use super::{AccessMode, DEF_FILE_MODE, File};
8use crate::fs::dir::DirEntry;
9use crate::fs::file::{Create, CreateError, CreateIfMissing, CreateOrEmpty, CreateTemp, CreateUnlinked, NoCreate, OpenError, OpenMode, Permanent, ReadOnly, ReadWrite, TempError, Temporary, Write, WriteOnly};
10use crate::fs::{Abs, Directory, Fd, FileType, OwnedPath, Rel};
11use crate::util;
12use crate::util::fmt::DebugRaw;
13
14pub(crate) const EXTRA_FLAGS_MASK: c_int = !(
15    O_APPEND | O_NOATIME | O_NOFOLLOW | O_SYNC | O_CREAT | O_EXCL | O_TRUNC | O_TMPFILE | O_DIRECTORY
16);
17
18/// A builder struct to help with opening files, using customizable options and logical defaults.
19/// Available via [`File::options`] to avoid additional use statements.
20// TODO: More docs here.
21#[derive(Clone)]
22pub struct OpenOptions<Access: AccessMode, Open: OpenMode> {
23    pub(crate) _access: PhantomData<fn() -> Access>,
24    pub(crate) _open: PhantomData<fn() -> Open>,
25    pub(crate) flags: c_int,
26    pub(crate) mode: c_int,
27}
28
29macro_rules! set_flag {
30    ($self:ident, $value:expr, $flag:expr) => {
31        if $value {
32            $self.flags |= $flag;
33        } else {
34            $self.flags &= !$flag;
35        }
36    };
37}
38
39macro_rules! get_flag {
40    ($self:ident, $flag:expr) => {
41        $self.flags & $flag != 0
42    };
43}
44
45impl<A: AccessMode, O: OpenMode> OpenOptions<A, O> {
46    pub(crate) const fn flags(&self) -> c_int {
47        self.flags | A::FLAGS | O::FLAGS
48    }
49
50    pub fn new() -> OpenOptions<A, O> {
51        OpenOptions::<A, O>::default()
52    }
53
54    pub const fn no_create(self) -> OpenOptions<A, NoCreate> {
55        OpenOptions::<A, NoCreate> {
56            _open: PhantomData,
57            ..self
58        }
59    }
60
61    pub const fn create_if_missing(self) -> OpenOptions<A, CreateIfMissing> {
62        OpenOptions::<A, CreateIfMissing> {
63            _open: PhantomData,
64            ..self
65        }
66    }
67
68    pub const fn create_or_empty(self) -> OpenOptions<A, CreateOrEmpty> {
69        OpenOptions::<A, CreateOrEmpty> {
70            _open: PhantomData,
71            ..self
72        }
73    }
74
75    pub const fn create(self) -> OpenOptions<A, Create> {
76        OpenOptions::<A, Create> {
77            _open: PhantomData,
78            ..self
79        }
80    }
81
82    pub const fn write_only(self) -> OpenOptions<WriteOnly, O> {
83        OpenOptions::<WriteOnly, O> {
84            _access: PhantomData,
85            ..self
86        }
87    }
88
89    pub const fn read_write(self) -> OpenOptions<ReadWrite, O> {
90        OpenOptions::<ReadWrite, O> {
91            _access: PhantomData,
92            ..self
93        }
94    }
95
96    pub const fn mode(&mut self, value: u16) -> &mut Self {
97        self.mode = value as c_int;
98        self
99    }
100
101    pub const fn append(&mut self, value: bool) -> &mut Self {
102        set_flag!(self, value, O_APPEND);
103        self
104    }
105
106    pub const fn force_sync(&mut self, value: bool) -> &mut Self {
107        set_flag!(self, value, O_SYNC);
108        self
109    }
110
111    pub const fn update_access_time(&mut self, value: bool) -> &mut Self {
112        set_flag!(self, !value, O_NOATIME);
113        self
114    }
115
116    pub const fn follow_links(&mut self, value: bool) -> &mut Self {
117        set_flag!(self, !value, O_NOFOLLOW);
118        self
119    }
120
121    pub const unsafe fn extra_flags(&mut self, value: i32) -> &mut Self {
122        self.flags |= value & EXTRA_FLAGS_MASK;
123        self
124    }
125}
126
127impl<A: AccessMode, O: Permanent> OpenOptions<A, O> {
128    pub const fn read_only(self) -> OpenOptions<ReadOnly, O> {
129        OpenOptions::<ReadOnly, O> {
130            _access: PhantomData,
131            ..self
132        }
133    }
134}
135
136impl<A: Write, O: OpenMode> OpenOptions<A, O> {
137    pub const fn create_temp(self) -> OpenOptions<A, CreateTemp> {
138        OpenOptions::<A, CreateTemp> {
139            _open: PhantomData,
140            ..self
141        }
142    }
143
144    pub const fn create_unlinked(self) -> OpenOptions<A, CreateUnlinked> {
145        OpenOptions::<A, CreateUnlinked> {
146            _open: PhantomData,
147            ..self
148        }
149    }
150}
151
152macro_rules! impl_open {
153    ($mode:ty) => {
154        impl<A: AccessMode> OpenOptions<A, $mode> {
155            pub fn open<P: Into<OwnedPath<Abs>>>(&self, file_path: P) -> Result<File<A>, OpenError> {
156                match Fd::open(file_path, self.flags(), self.mode) {
157                    Ok(fd) => Ok(File::<A> {
158                        _access: PhantomData,
159                        fd: fd.assert_type(FileType::Regular)?,
160                    }),
161                    Err(e) => Err(OpenError::interpret_raw_error(e)),
162                }
163            }
164
165            pub fn open_rel<P: Into<OwnedPath<Rel>>>(
166                &self,
167                relative_to: &Directory,
168                file_path: P
169            ) -> Result<File<A>, OpenError> {
170                match Fd::open_rel(relative_to, file_path, self.flags(), self.mode) {
171                    Ok(fd) => Ok(File::<A> {
172                        _access: PhantomData,
173                        fd: fd.assert_type(FileType::Regular)?,
174                    }),
175                    Err(e) => Err(OpenError::interpret_raw_error(e)),
176                }
177            }
178
179            pub fn open_dir_entry(&self, dir_ent: &DirEntry) -> Result<File<A>, OpenError> {
180                // TODO: This could take ownership of DirEntry instead of cloning?
181                self.open_rel(dir_ent.parent, dir_ent.path.clone())
182            }
183        }
184    };
185    ($mode:ty, $($a:ty),+) => {
186        impl_open!($mode);
187        impl_open!($($a),+);
188    };
189}
190
191macro_rules! impl_create {
192    ($mode:ty) => {
193        impl<A: AccessMode> OpenOptions<A, $mode> {
194            pub fn open<P: Into<OwnedPath<Abs>>>(&self, file_path: P) -> Result<File<A>, CreateError> {
195                match Fd::open(file_path, self.flags(), self.mode) {
196                    Ok(fd) => Ok(File::<A> {
197                        _access: PhantomData,
198                        fd,
199                    }),
200                    Err(e) => Err(CreateError::interpret_raw_error(e)),
201                }
202            }
203
204            pub fn open_rel<P: Into<OwnedPath<Rel>>>(
205                &self,
206                relative_to: &Directory,
207                file_path: P
208            ) -> Result<File<A>, CreateError> {
209                match Fd::open_rel(relative_to, file_path, self.flags(), self.mode) {
210                    Ok(fd) => Ok(File::<A> {
211                        _access: PhantomData,
212                        fd,
213                    }),
214                    Err(e) => Err(CreateError::interpret_raw_error(e)),
215                }
216            }
217        }
218    };
219    ($mode:ty, $($a:ty),+) => {
220        impl_create!($mode);
221        impl_create!($($a),+);
222    };
223}
224
225macro_rules! impl_create_temp {
226    ($mode:ty) => {
227        impl<A: AccessMode> OpenOptions<A, $mode> {
228            pub fn open<P: Into<OwnedPath<Abs>>>(&self, dir_path: P) -> Result<File<A>, TempError> {
229                match Fd::open(dir_path, self.flags(), self.mode) {
230                    Ok(fd) => Ok(File::<A> {
231                        _access: PhantomData,
232                        fd,
233                    }),
234                    Err(e) => Err(TempError::interpret_raw_error(e)),
235                }
236            }
237
238            pub fn open_rel(
239                &self,
240                relative_to: &Directory
241            ) -> Result<File<A>, TempError> {
242                match Fd::open_rel(
243                    relative_to,
244                    unsafe { OwnedPath::dot_slash_dot() },
245                    self.flags(),
246                    self.mode
247                ) {
248                    Ok(fd) => Ok(File::<A> {
249                        _access: PhantomData,
250                        fd,
251                    }),
252                    Err(e) => Err(TempError::interpret_raw_error(e)),
253                }
254            }
255        }
256    };
257    ($mode:ty, $($a:ty),+) => {
258        impl_create_temp!($mode);
259        impl_create_temp!($($a),+);
260    };
261}
262
263impl_open! {
264    NoCreate,
265    CreateIfMissing,
266    CreateOrEmpty
267}
268
269impl_create! {
270    Create
271}
272
273impl_create_temp! {
274    CreateTemp,
275    CreateUnlinked
276}
277
278// The Default derive macro doesn't like my spooky zero-variant enums.
279impl<A: AccessMode, O: OpenMode> Default for OpenOptions<A, O> {
280    fn default() -> Self {
281        Self {
282            _access: Default::default(),
283            _open: Default::default(),
284            mode: DEF_FILE_MODE,
285            flags: 0x0,
286        }
287    }
288}
289
290impl<A: AccessMode, O: OpenMode> Debug for OpenOptions<A, O> {
291    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
292        f.debug_struct("OpenOptions")
293            .field("<access>", &util::fmt::raw_type_name::<A>())
294            .field("<open>", &util::fmt::raw_type_name::<O>())
295            .field("mode", &DebugRaw(format!("0o{:o}", self.mode)))
296            .field("append", &get_flag!(self, O_APPEND))
297            .field("force_sync", &get_flag!(self, O_SYNC))
298            .field("update_access_time", &!get_flag!(self, O_NOATIME))
299            .field("follow_links", &!get_flag!(self, O_NOFOLLOW))
300            .field("extra_flags", &DebugRaw(format!("0x{:x}", self.flags & EXTRA_FLAGS_MASK)))
301            .finish()
302    }
303}