1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
use std::fs; use std::io; use std::path::Path; use cfg_if::cfg_if; use super::File; use crate::future::Future; use crate::task::blocking; /// Options and flags which for configuring how a file is opened. /// /// This builder exposes the ability to configure how a [`File`] is opened and what operations are /// permitted on the open file. The [`File::open`] and [`File::create`] methods are aliases for /// commonly used options with this builder. /// /// Generally speaking, when using `OpenOptions`, you'll first call [`new`], then chain calls to /// methods to set each option, then call [`open`], passing the path of the file you're trying to /// open. This will give you a [`File`] inside that you can further operate on. /// /// This type is an async version of [`std::fs::OpenOptions`]. /// /// [`new`]: struct.OpenOptions.html#method.new /// [`open`]: struct.OpenOptions.html#method.open /// [`File`]: struct.File.html /// [`File::open`]: struct.File.html#method.open /// [`File::create`]: struct.File.html#method.create /// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html /// /// # Examples /// /// Opening a file for reading: /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .read(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` /// /// Opening a file for both reading and writing, creating it if it doesn't exist: /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .read(true) /// .write(true) /// .create(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` #[derive(Clone, Debug)] pub struct OpenOptions(fs::OpenOptions); impl OpenOptions { /// Creates a blank new set of options. /// /// All options are initially set to `false`. /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .read(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn new() -> OpenOptions { OpenOptions(fs::OpenOptions::new()) } /// Sets the option for read access. /// /// This option, when `true`, will indicate that the file should be readable if opened. /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .read(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn read(&mut self, read: bool) -> &mut OpenOptions { self.0.read(read); self } /// Sets the option for write access. /// /// This option, when `true`, will indicate that the file should be writable if opened. /// /// If the file already exists, any write calls on it will overwrite its contents, without /// truncating it. /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .write(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn write(&mut self, write: bool) -> &mut OpenOptions { self.0.write(write); self } /// Sets the option for append mode. /// /// This option, when `true`, means that writes will append to a file instead of overwriting /// previous contents. Note that setting `.write(true).append(true)` has the same effect as /// setting only `.append(true)`. /// /// For most filesystems, the operating system guarantees that all writes are atomic: no writes /// get mangled because another process writes at the same time. /// /// One maybe obvious note when using append mode: make sure that all data that belongs /// together is written to the file in one operation. This can be done by concatenating strings /// before writing them, or using a buffered writer (with a buffer of adequate size), and /// flushing when the message is complete. /// /// If a file is opened with both read and append access, beware that after opening and after /// every write, the position for reading may be set at the end of the file. So, before /// writing, save the current position by seeking with a zero offset, and restore it before the /// next read. /// /// ## Note /// /// This function doesn't create the file if it doesn't exist. Use the [`create`] method to do /// so. /// /// [`create`]: #method.create /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .append(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn append(&mut self, append: bool) -> &mut OpenOptions { self.0.append(append); self } /// Sets the option for truncating a previous file. /// /// If a file is successfully opened with this option set, it will truncate the file to 0 /// length if it already exists. /// /// The file must be opened with write access for truncation to work. /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .write(true) /// .truncate(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { self.0.truncate(truncate); self } /// Sets the option for creating a new file. /// /// This option indicates whether a new file will be created if the file does not yet exist. /// /// In order for the file to be created, [`write`] or [`append`] access must be used. /// /// [`write`]: #method.write /// [`append`]: #method.append /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .write(true) /// .create(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn create(&mut self, create: bool) -> &mut OpenOptions { self.0.create(create); self } /// Sets the option to always create a new file. /// /// This option indicates whether a new file will be created. No file is allowed to exist at /// the target location, also no (dangling) symlink. /// /// This option is useful because it is atomic. Otherwise, between checking whether a file /// exists and creating a new one, the file may have been created by another process (a TOCTOU /// race condition / attack). /// /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are ignored. /// /// The file must be opened with write or append access in order to create a new file. /// /// [`.create()`]: #method.create /// [`.truncate()`]: #method.truncate /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new() /// .write(true) /// .create_new(true) /// .open("a.txt") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions { self.0.create_new(create_new); self } /// Opens a file at specified path with the configured options. /// /// # Errors /// /// This function will return an error under a number of different circumstances. Some of these /// error conditions are listed here, together with their [`ErrorKind`]. The mapping to /// [`ErrorKind`]s is not part of the compatibility contract of the function, especially the /// `Other` kind might change to more specific kinds in the future. /// /// * [`NotFound`]: The specified file does not exist and neither `create` or `create_new` is /// set. /// * [`NotFound`]: One of the directory components of the file path does not exist. /// * [`PermissionDenied`]: The user lacks permission to get the specified access rights for /// the file. /// * [`PermissionDenied`]: The user lacks permission to open one of the directory components /// of the specified path. /// * [`AlreadyExists`]: `create_new` was specified and the file already exists. /// * [`InvalidInput`]: Invalid combinations of open options (truncate without write access, no /// access mode set, etc.). /// * [`Other`]: One of the directory components of the specified file path was not, in fact, a /// directory. /// * [`Other`]: Filesystem-level errors: full disk, write permission requested on a read-only /// file system, exceeded disk quota, too many open files, too long filename, too many /// symbolic links in the specified path (Unix-like systems only), etc. /// /// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html /// [`AlreadyExists`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.AlreadyExists /// [`InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput /// [`NotFound`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.NotFound /// [`Other`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.Other /// [`PermissionDenied`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.PermissionDenied /// /// # Examples /// /// ```no_run /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; /// /// let file = OpenOptions::new().open("a.txt").await?; /// # /// # Ok(()) }) } /// ``` pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> { let path = path.as_ref().to_owned(); let options = self.0.clone(); async move { blocking::spawn(async move { options.open(path).map(|f| f.into()) }).await } } } cfg_if! { if #[cfg(feature = "docs")] { use crate::os::unix::fs::OpenOptionsExt; } else if #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; } } #[cfg_attr(feature = "docs", doc(cfg(unix)))] cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl OpenOptionsExt for OpenOptions { fn mode(&mut self, mode: u32) -> &mut Self { self.0.mode(mode); self } fn custom_flags(&mut self, flags: i32) -> &mut Self { self.0.custom_flags(flags); self } } } }