1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::fs::OpenOptions as SyncOpenOptions; 15 use std::io; 16 use std::path::Path; 17 18 use crate::fs::{async_op, File}; 19 20 /// An asynchronous version of the [`std::fs::OpenOptions`]; 21 /// 22 /// Options and flags which can be used to configure how a file is opened. 23 /// 24 /// ```no_run 25 /// use ylong_runtime::fs::OpenOptions; 26 /// 27 /// async fn open_with_options() { 28 /// let file = OpenOptions::new() 29 /// .read(true) 30 /// .write(true) 31 /// .create(true) 32 /// .open("foo.txt") 33 /// .await; 34 /// } 35 /// ``` 36 #[derive(Clone, Debug)] 37 pub struct OpenOptions(SyncOpenOptions); 38 39 impl OpenOptions { 40 /// Creates a blank new set of options ready for configuration when opening 41 /// a file. 42 /// 43 /// All options are initially set to `false` just like 44 /// [`std::fs::OpenOptions::new`]. 45 /// 46 /// # Examples 47 /// 48 /// ```no_run 49 /// use ylong_runtime::fs::OpenOptions; 50 /// 51 /// async fn create_option() { 52 /// let mut options = OpenOptions::new(); 53 /// let file = options.read(true).open("foo.txt").await; 54 /// } 55 /// ``` 56 // Prevent to increase binary size and thus mask this warning. 57 #[allow(clippy::new_without_default)] new() -> OpenOptions58 pub fn new() -> OpenOptions { 59 OpenOptions(SyncOpenOptions::new()) 60 } 61 62 /// Sets the option for file read access. 63 /// 64 /// This option, when true, will indicate that the file should be 65 /// `read`-able if opened. 66 /// 67 /// This method's behavior is the same as [`std::fs::OpenOptions::read`]. 68 /// 69 /// # Examples 70 /// 71 /// ```no_run 72 /// use ylong_runtime::fs::OpenOptions; 73 /// 74 /// async fn open_with_read() { 75 /// let file = OpenOptions::new().read(true).open("foo.txt").await; 76 /// } 77 /// ``` read(&mut self, read: bool) -> &mut OpenOptions78 pub fn read(&mut self, read: bool) -> &mut OpenOptions { 79 self.0.read(read); 80 self 81 } 82 83 /// Sets the option for file write access. 84 /// 85 /// This option, when true, will indicate that the file should be 86 /// `write`-able if opened. 87 /// 88 /// If the file already exists, any write calls on it will overwrite its 89 /// previous contents, without truncating it. 90 /// 91 /// This method's behavior is the same as [`std::fs::OpenOptions::write`]. 92 /// 93 /// # Examples 94 /// 95 /// ```no_run 96 /// use ylong_runtime::fs::OpenOptions; 97 /// 98 /// async fn open_with_write() { 99 /// let file = OpenOptions::new().write(true).open("foo.txt").await; 100 /// } 101 /// ``` write(&mut self, write: bool) -> &mut OpenOptions102 pub fn write(&mut self, write: bool) -> &mut OpenOptions { 103 self.0.write(write); 104 self 105 } 106 107 /// Sets the option for the file append mode. 108 /// 109 /// This option, when true, means that writes will append to a file instead 110 /// of overwriting previous contents. 111 /// 112 /// Note that when setting `.append(true)`, file write access will also be 113 /// turned on 114 /// 115 /// For most filesystems, the operating system guarantees that all writes 116 /// are atomic: no writes get mangled because another thread or process 117 /// writes at the same time. 118 /// 119 /// User should write all data that belongs together in one operation during 120 /// appending. This can be done by concatenating strings before passing 121 /// them to [`write()`], or using a buffered writer (with a buffer of 122 /// adequate size), and calling [`flush()`] when the message is written 123 /// completely. 124 /// 125 /// If a file is opened with both read and append access, beware that after 126 /// opening and every write, the position for reading may be set at the end 127 /// of the file. So, before writing, save the current position, and 128 /// restore it before the next read. 129 /// 130 /// This method's behavior is the same as [`std::fs::OpenOptions::append`]. 131 /// 132 /// ## Note 133 /// 134 /// This method doesn't create the file if it doesn't exist. Use the 135 /// [`OpenOptions::create`] method to do so. 136 /// 137 /// [`write()`]: crate::io::AsyncWriteExt::write 138 /// [`flush()`]: crate::io::AsyncWrite::poll_flush 139 /// [seek]: crate::io::AsyncSeekExt::seek 140 /// 141 /// # Examples 142 /// 143 /// ```no_run 144 /// use ylong_runtime::fs::OpenOptions; 145 /// 146 /// async fn open_with_append() { 147 /// let file = OpenOptions::new().append(true).open("foo.txt").await; 148 /// } 149 /// ``` append(&mut self, append: bool) -> &mut OpenOptions150 pub fn append(&mut self, append: bool) -> &mut OpenOptions { 151 self.0.append(append); 152 self 153 } 154 155 /// Sets the option for truncating a file's previous content. 156 /// 157 /// If a file is successfully opened with this option set, it will truncate 158 /// the file to 0 length if it already exists. Any already-existed content 159 /// in this file will be dropped. 160 /// 161 /// The file must be opened with write access for truncate to work, which is 162 /// different from append mode. 163 /// 164 /// This method's behavior is the same as 165 /// [`std::fs::OpenOptions::truncate`]. 166 /// 167 /// # Examples 168 /// 169 /// ```no_run 170 /// use ylong_runtime::fs::OpenOptions; 171 /// 172 /// async fn open_with_truncate() { 173 /// let file = OpenOptions::new() 174 /// .write(true) 175 /// .truncate(true) 176 /// .open("foo.txt") 177 /// .await; 178 /// } 179 /// ``` truncate(&mut self, truncate: bool) -> &mut Self180 pub fn truncate(&mut self, truncate: bool) -> &mut Self { 181 self.0.truncate(truncate); 182 self 183 } 184 185 /// Sets the option to create a new file if it doesn't already exist, or 186 /// simply open it if it does exist. 187 /// 188 /// In order for the file to be created, [`OpenOptions::write`] or 189 /// [`OpenOptions::append`] access must be set to true. 190 /// 191 /// This method's behavior is the same as [`std::fs::OpenOptions::create`]. 192 /// 193 /// # Examples 194 /// 195 /// ```no_run 196 /// use ylong_runtime::fs::OpenOptions; 197 /// 198 /// async fn open_with_create() { 199 /// let file = OpenOptions::new() 200 /// .write(true) 201 /// .create(true) 202 /// .open("foo.txt") 203 /// .await; 204 /// } 205 /// ``` create(&mut self, create: bool) -> &mut Self206 pub fn create(&mut self, create: bool) -> &mut Self { 207 self.0.create(create); 208 self 209 } 210 211 /// Sets the option to create a new file. 212 /// 213 /// If the file already exists, opening the file with the option set will 214 /// cause an error. 215 /// 216 /// No file is allowed to exist at the target location, also no (dangling) 217 /// symlink. In this way, if the call succeeds, the file returned is 218 /// guaranteed to be new. 219 /// 220 /// This option guarantees the operation of checking whether a file exists 221 /// and creating a new one is atomic. 222 /// 223 /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are 224 /// ignored. 225 /// 226 /// The file must be opened with write or append mode in order to create 227 /// a new file. 228 /// 229 /// [`.create(true)`]: OpenOptions::create 230 /// [`.truncate(true)`]: OpenOptions::truncate 231 /// 232 /// This method's behavior is the same as 233 /// [`std::fs::OpenOptions::create_new`]. 234 /// 235 /// # Examples 236 /// 237 /// ```no_run 238 /// use ylong_runtime::fs::OpenOptions; 239 /// 240 /// async fn open_with_create_new() { 241 /// let file = OpenOptions::new() 242 /// .write(true) 243 /// .create_new(true) 244 /// .open("foo.txt") 245 /// .await; 246 /// } 247 /// ``` create_new(&mut self, create_new: bool) -> &mut Self248 pub fn create_new(&mut self, create_new: bool) -> &mut Self { 249 self.0.create_new(create_new); 250 self 251 } 252 253 /// Asynchronously opens a file at `path` with the options specified by 254 /// `self`. 255 /// 256 /// # Errors 257 /// 258 /// This method's behavior is the same as [`std::fs::OpenOptions::open`]. 259 /// 260 /// * [`NotFound`]: The specified file does not exist and neither `create` 261 /// or `create_new` is set. 262 /// * [`NotFound`]: One of the directory components of the file path doesn't 263 /// exist. 264 /// * [`PermissionDenied`]: The user doesn't has permission to get the 265 /// specified access rights for the file. 266 /// * [`PermissionDenied`]: The user doesn't has permission to open one of 267 /// the directory components of the specified path. 268 /// * [`AlreadyExists`]: `create_new` was specified and the file already 269 /// exists. 270 /// * [`InvalidInput`]: Invalid combinations of open options (truncate 271 /// without write access, no access mode set, etc.). 272 /// 273 /// The following errors don't match any existing [`io::ErrorKind`] at the 274 /// moment: 275 /// * One of the directory components of the specified file path was not, in 276 /// fact, a directory. 277 /// * Filesystem-level errors: full disk, write permission requested on a 278 /// read-only file system, exceeded disk quota, too many open files, too 279 /// long filename, too many symbolic links in the specified path 280 /// (Unix-like systems only), etc. 281 /// 282 /// [`NotFound`]: std::io::ErrorKind::NotFound 283 /// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied 284 /// [`AlreadyExists`]: std::io::ErrorKind::AlreadyExists 285 /// [`InvalidInput`]: std::io::ErrorKind::InvalidInput 286 /// # Examples 287 /// 288 /// ```no_run 289 /// use ylong_runtime::fs::OpenOptions; 290 /// 291 /// async fn option_open() { 292 /// let file = OpenOptions::new().read(true).open("foo.txt").await; 293 /// } 294 /// ``` open<P: AsRef<Path>>(&self, path: P) -> io::Result<File>295 pub async fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> { 296 let path = path.as_ref().to_owned(); 297 let options = self.0.clone(); 298 let file = async_op(move || options.open(path)).await?; 299 Ok(File::new(file)) 300 } 301 } 302 #[cfg(all(test, target_os = "linux"))] 303 mod test { 304 use crate::fs::OpenOptions; 305 306 /// UT test cases for Openoption. 307 308 /// # Brief 309 /// 1. Call `read`、`write`、`append`、`truncate`、`create`、`create_new` 310 /// function, passing in the specified parameters. 311 /// 2. Check if the settings are correct. 312 #[cfg(target_os = "linux")] 313 #[test] ut_set_openoption()314 fn ut_set_openoption() { 315 let mut option = OpenOptions::new(); 316 option.read(true); 317 option.write(true); 318 option.append(true); 319 option.truncate(true); 320 option.create(true); 321 option.create_new(true); 322 assert_eq!("OpenOptions(OpenOptions { read: true, write: true, append: true, truncate: true, create: true, create_new: true, custom_flags: 0, mode: 438 })", format!("{:?}", option.0)) 323 } 324 } 325