standard_lib/fs/file/
options.rs

1use std::fmt;
2use std::ffi::CString;
3use std::fmt::{Debug, Formatter};
4use std::io::RawOsError;
5use std::marker::PhantomData;
6
7use libc::{O_APPEND, O_CREAT, O_EXCL, O_NOATIME, O_NOFOLLOW, O_SYNC, O_TRUNC, c_int};
8
9use super::{File, AccessMode};
10use crate::fs::dir::DirEntry;
11use crate::fs::path::{Abs, Path};
12use crate::fs::{Directory, Fd, Rel};
13use crate::util;
14
15/// A builder struct to help with opening files, using customizable options and logical defaults.
16/// Available via [`File::options`] to avoid additional use statements.
17// TODO: More docs here.
18#[derive(Clone)]
19pub struct OpenOptions<Access: AccessMode> {
20    pub(crate) _access: PhantomData<fn() -> Access>,
21    pub create: Option<Create>,
22    pub mode: Option<u16>,
23    pub append: Option<bool>,
24    pub force_sync: Option<bool>,
25    pub update_access_time: Option<bool>,
26    pub follow_links: Option<bool>,
27    pub extra_flags: Option<i32>,
28}
29
30#[derive(Debug, Clone, Copy, Default)]
31pub enum Create {
32    No,
33    #[default]
34    IfAbsent,
35    OrClear,
36    Require,
37}
38
39impl<A: AccessMode> OpenOptions<A> {
40    pub(crate) fn flags(&self) -> c_int {
41        let mut flags = A::FLAGS;
42        match &self.create.unwrap_or_default() {
43            Create::No => (),
44            Create::IfAbsent => flags |= O_CREAT,
45            Create::OrClear =>  flags |= O_CREAT | O_TRUNC,
46            Create::Require =>  flags |= O_CREAT | O_EXCL,
47        }
48        if self.append.unwrap_or(false) {
49            flags |= O_APPEND;
50        }
51        if self.force_sync.unwrap_or(false) {
52            flags |= O_SYNC;
53        }
54        if !self.update_access_time.unwrap_or(true) {
55            flags |= O_NOATIME;
56        }
57        if !self.follow_links.unwrap_or(true) {
58            flags |= O_NOFOLLOW;
59        }
60        flags | self.extra_flags.unwrap_or_default()
61    }
62
63    pub fn new() -> OpenOptions<A> {
64        OpenOptions::<A>::default()
65    }
66
67    pub fn open<P: AsRef<Path<Abs>>>(&self, file_path: P) -> Result<File<A>, RawOsError> {
68        let pathname = CString::from(file_path.as_ref().to_owned());
69        // TODO: Permission builder of some type?
70        let mode = self.mode.unwrap_or(0o644) as c_int;
71
72        match unsafe { libc::open(pathname.as_ptr().cast(), self.flags(), mode) } {
73            -1 => Err(util::fs::err_no()),
74            fd => Ok(File::<A> {
75                _access: PhantomData,
76                fd: Fd(fd),
77            }),
78        }
79    }
80
81    pub fn open_rel<P: AsRef<Path<Rel>>>(
82        &self,
83        relative_to: &Directory,
84        file_path: P
85    ) -> Result<File<A>, RawOsError> {
86        let pathname = CString::from(file_path.as_ref().to_owned());
87        let mode = self.mode.unwrap_or(0o644) as c_int;
88
89        match unsafe { libc::openat(
90            *relative_to.fd,
91            // Skip the leading '/' so that the path is considered relative.
92            pathname.as_ptr().add(1).cast(),
93            self.flags(),
94            mode
95        ) } {
96            -1 => Err(util::fs::err_no()),
97            fd => Ok(File::<A> {
98                _access: PhantomData,
99                fd: Fd(fd),
100            }),
101        }
102    }
103
104    pub fn open_dir_entry(&self, dir_ent: &DirEntry) -> Result<File<A>, RawOsError> {
105        self.open_rel(dir_ent.parent, &dir_ent.path)
106    }
107
108    pub const fn create_mode(&mut self, value: Create) -> &mut Self {
109        self.create = Some(value);
110        self
111    }
112
113    pub const fn no_create(&mut self) -> &mut Self {
114        self.create = Some(Create::No);
115        self
116    }
117
118    pub const fn create_if_absent(&mut self) -> &mut Self {
119        self.create = Some(Create::IfAbsent);
120        self
121    }
122
123    pub const fn create_or_clear(&mut self) -> &mut Self {
124        self.create = Some(Create::OrClear);
125        self
126    }
127
128    pub const fn create_only(&mut self) -> &mut Self {
129        self.create = Some(Create::Require);
130        self
131    }
132
133    pub const fn mode(&mut self, value: u16) -> &mut Self {
134        self.mode = Some(value);
135        self
136    }
137
138    pub const fn append(&mut self, value: bool) -> &mut Self {
139        self.append = Some(value);
140        self
141    }
142
143    pub const fn force_sync(&mut self, value: bool) -> &mut Self {
144        self.force_sync = Some(value);
145        self
146    }
147
148    pub const fn update_access_time(&mut self, value: bool) -> &mut Self {
149        self.update_access_time = Some(value);
150        self
151    }
152
153    pub const fn follow_links(&mut self, value: bool) -> &mut Self {
154        self.follow_links = Some(value);
155        self
156    }
157
158    pub const fn extra_flags(&mut self, value: i32) -> &mut Self {
159        self.extra_flags = Some(value);
160        self
161    }
162}
163
164// The Default derive macro doesn't like my spooky zero-variant enums.
165impl<A: AccessMode> Default for OpenOptions<A> {
166    fn default() -> Self {
167        Self {
168            _access: Default::default(),
169            create: Default::default(),
170            mode: Default::default(),
171            append: Default::default(),
172            force_sync: Default::default(),
173            update_access_time: Default::default(),
174            follow_links: Default::default(),
175            extra_flags: Default::default()
176        }
177    }
178}
179
180impl<A: AccessMode> Debug for OpenOptions<A> {
181    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
182        f.debug_struct("OpenOptions")
183            .field("<access>", &util::fmt::raw_type_name::<A>())
184            .field("create", &self.create)
185            .field("mode", &self.mode)
186            .field("append", &self.append)
187            .field("force_sync", &self.force_sync)
188            .field("update_access_time", &self.update_access_time)
189            .field("follow_links", &self.follow_links)
190            .field("extra_flags", &self.extra_flags)
191            .finish()
192    }
193}