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#[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 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
278impl<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}