]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Filesystem manipulation operations | |
12 | //! | |
13 | //! This module contains basic methods to manipulate the contents of the local | |
14 | //! filesystem. All methods in this module represent cross-platform filesystem | |
15 | //! operations. Extra platform-specific functionality can be found in the | |
16 | //! extension traits of `std::os::$platform`. | |
17 | ||
c34b1796 | 18 | #![stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
19 | |
20 | use core::prelude::*; | |
21 | ||
d9579d0f AL |
22 | use fmt; |
23 | use ffi::OsString; | |
85aaf69f | 24 | use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write}; |
c34b1796 | 25 | use path::{Path, PathBuf}; |
d9579d0f | 26 | use sys::fs as fs_imp; |
85aaf69f SL |
27 | use sys_common::{AsInnerMut, FromInner, AsInner}; |
28 | use vec::Vec; | |
29 | ||
30 | /// A reference to an open file on the filesystem. | |
31 | /// | |
32 | /// An instance of a `File` can be read and/or written depending on what options | |
33 | /// it was opened with. Files also implement `Seek` to alter the logical cursor | |
34 | /// that the file contains internally. | |
35 | /// | |
c34b1796 | 36 | /// # Examples |
85aaf69f SL |
37 | /// |
38 | /// ```no_run | |
39 | /// use std::io::prelude::*; | |
40 | /// use std::fs::File; | |
41 | /// | |
42 | /// # fn foo() -> std::io::Result<()> { | |
43 | /// let mut f = try!(File::create("foo.txt")); | |
44 | /// try!(f.write_all(b"Hello, world!")); | |
45 | /// | |
46 | /// let mut f = try!(File::open("foo.txt")); | |
47 | /// let mut s = String::new(); | |
48 | /// try!(f.read_to_string(&mut s)); | |
49 | /// assert_eq!(s, "Hello, world!"); | |
50 | /// # Ok(()) | |
51 | /// # } | |
52 | /// ``` | |
c34b1796 | 53 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
54 | pub struct File { |
55 | inner: fs_imp::File, | |
85aaf69f SL |
56 | } |
57 | ||
58 | /// Metadata information about a file. | |
59 | /// | |
60 | /// This structure is returned from the `metadata` function or method and | |
61 | /// represents known metadata about a file such as its permissions, size, | |
62 | /// modification times, etc. | |
c34b1796 | 63 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
64 | pub struct Metadata(fs_imp::FileAttr); |
65 | ||
66 | /// Iterator over the entries in a directory. | |
67 | /// | |
68 | /// This iterator is returned from the `read_dir` function of this module and | |
69 | /// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry` | |
70 | /// information like the entry's path and possibly other metadata can be | |
71 | /// learned. | |
c34b1796 AL |
72 | /// |
73 | /// # Failure | |
74 | /// | |
75 | /// This `io::Result` will be an `Err` if there's some sort of intermittent | |
76 | /// IO error during iteration. | |
77 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
78 | pub struct ReadDir(fs_imp::ReadDir); |
79 | ||
80 | /// Entries returned by the `ReadDir` iterator. | |
81 | /// | |
82 | /// An instance of `DirEntry` represents an entry inside of a directory on the | |
83 | /// filesystem. Each entry can be inspected via methods to learn about the full | |
84 | /// path or possibly other metadata through per-platform extension traits. | |
c34b1796 | 85 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
86 | pub struct DirEntry(fs_imp::DirEntry); |
87 | ||
88 | /// An iterator that recursively walks over the contents of a directory. | |
c34b1796 AL |
89 | #[unstable(feature = "fs_walk", |
90 | reason = "the precise semantics and defaults for a recursive walk \ | |
91 | may change and this may end up accounting for files such \ | |
92 | as symlinks differently")] | |
85aaf69f SL |
93 | pub struct WalkDir { |
94 | cur: Option<ReadDir>, | |
95 | stack: Vec<io::Result<ReadDir>>, | |
96 | } | |
97 | ||
98 | /// Options and flags which can be used to configure how a file is opened. | |
99 | /// | |
100 | /// This builder exposes the ability to configure how a `File` is opened and | |
101 | /// what operations are permitted on the open file. The `File::open` and | |
102 | /// `File::create` methods are aliases for commonly used options using this | |
103 | /// builder. | |
9346a6ac AL |
104 | /// |
105 | /// Generally speaking, when using `OpenOptions`, you'll first call `new()`, | |
106 | /// then chain calls to methods to set each option, then call `open()`, passing | |
107 | /// the path of the file you're trying to open. This will give you a | |
108 | /// [`io::Result`][result] with a [`File`][file] inside that you can further | |
109 | /// operate on. | |
110 | /// | |
111 | /// [result]: ../io/type.Result.html | |
112 | /// [file]: struct.File.html | |
113 | /// | |
114 | /// # Examples | |
115 | /// | |
116 | /// Opening a file to read: | |
117 | /// | |
118 | /// ```no_run | |
119 | /// use std::fs::OpenOptions; | |
120 | /// | |
121 | /// let file = OpenOptions::new().read(true).open("foo.txt"); | |
122 | /// ``` | |
123 | /// | |
124 | /// Opening a file for both reading and writing, as well as creating it if it | |
125 | /// doesn't exist: | |
126 | /// | |
127 | /// ```no_run | |
128 | /// use std::fs::OpenOptions; | |
129 | /// | |
130 | /// let file = OpenOptions::new() | |
131 | /// .read(true) | |
132 | /// .write(true) | |
133 | /// .create(true) | |
134 | /// .open("foo.txt"); | |
135 | /// ``` | |
85aaf69f | 136 | #[derive(Clone)] |
c34b1796 | 137 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
138 | pub struct OpenOptions(fs_imp::OpenOptions); |
139 | ||
140 | /// Representation of the various permissions on a file. | |
141 | /// | |
142 | /// This module only currently provides one bit of information, `readonly`, | |
143 | /// which is exposed on all currently supported platforms. Unix-specific | |
144 | /// functionality, such as mode bits, is available through the | |
145 | /// `os::unix::PermissionsExt` trait. | |
146 | #[derive(Clone, PartialEq, Eq, Debug)] | |
c34b1796 | 147 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
148 | pub struct Permissions(fs_imp::FilePermissions); |
149 | ||
d9579d0f AL |
150 | /// An structure representing a type of file with accessors for each file type. |
151 | #[stable(feature = "file_type", since = "1.1.0")] | |
152 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | |
153 | pub struct FileType(fs_imp::FileType); | |
154 | ||
155 | /// A builder used to create directories in various manners. | |
156 | /// | |
157 | /// This builder also supports platform-specific options. | |
158 | #[unstable(feature = "dir_builder", reason = "recently added API")] | |
159 | pub struct DirBuilder { | |
160 | inner: fs_imp::DirBuilder, | |
161 | recursive: bool, | |
162 | } | |
163 | ||
85aaf69f SL |
164 | impl File { |
165 | /// Attempts to open a file in read-only mode. | |
166 | /// | |
167 | /// See the `OpenOptions::open` method for more details. | |
168 | /// | |
169 | /// # Errors | |
170 | /// | |
171 | /// This function will return an error if `path` does not already exist. | |
172 | /// Other errors may also be returned according to `OpenOptions::open`. | |
c34b1796 AL |
173 | /// |
174 | /// # Examples | |
175 | /// | |
176 | /// ```no_run | |
177 | /// use std::fs::File; | |
178 | /// | |
179 | /// # fn foo() -> std::io::Result<()> { | |
180 | /// let mut f = try!(File::open("foo.txt")); | |
181 | /// # Ok(()) | |
182 | /// # } | |
183 | /// ``` | |
184 | #[stable(feature = "rust1", since = "1.0.0")] | |
185 | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
85aaf69f SL |
186 | OpenOptions::new().read(true).open(path) |
187 | } | |
188 | ||
9346a6ac | 189 | /// Opens a file in write-only mode. |
85aaf69f SL |
190 | /// |
191 | /// This function will create a file if it does not exist, | |
192 | /// and will truncate it if it does. | |
193 | /// | |
194 | /// See the `OpenOptions::open` function for more details. | |
c34b1796 AL |
195 | /// |
196 | /// # Examples | |
197 | /// | |
198 | /// ```no_run | |
199 | /// use std::fs::File; | |
200 | /// | |
201 | /// # fn foo() -> std::io::Result<()> { | |
202 | /// let mut f = try!(File::create("foo.txt")); | |
203 | /// # Ok(()) | |
204 | /// # } | |
205 | /// ``` | |
206 | #[stable(feature = "rust1", since = "1.0.0")] | |
207 | pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { | |
85aaf69f SL |
208 | OpenOptions::new().write(true).create(true).truncate(true).open(path) |
209 | } | |
210 | ||
9346a6ac | 211 | /// Attempts to sync all OS-internal metadata to disk. |
85aaf69f SL |
212 | /// |
213 | /// This function will attempt to ensure that all in-core data reaches the | |
214 | /// filesystem before returning. | |
c34b1796 AL |
215 | /// |
216 | /// # Examples | |
217 | /// | |
218 | /// ```no_run | |
219 | /// use std::fs::File; | |
220 | /// use std::io::prelude::*; | |
221 | /// | |
222 | /// # fn foo() -> std::io::Result<()> { | |
223 | /// let mut f = try!(File::create("foo.txt")); | |
224 | /// try!(f.write_all(b"Hello, world!")); | |
225 | /// | |
226 | /// try!(f.sync_all()); | |
227 | /// # Ok(()) | |
228 | /// # } | |
229 | /// ``` | |
230 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
231 | pub fn sync_all(&self) -> io::Result<()> { |
232 | self.inner.fsync() | |
233 | } | |
234 | ||
235 | /// This function is similar to `sync_all`, except that it may not | |
236 | /// synchronize file metadata to the filesystem. | |
237 | /// | |
238 | /// This is intended for use cases that must synchronize content, but don't | |
239 | /// need the metadata on disk. The goal of this method is to reduce disk | |
240 | /// operations. | |
241 | /// | |
242 | /// Note that some platforms may simply implement this in terms of | |
243 | /// `sync_all`. | |
c34b1796 AL |
244 | /// |
245 | /// # Examples | |
246 | /// | |
247 | /// ```no_run | |
248 | /// use std::fs::File; | |
249 | /// use std::io::prelude::*; | |
250 | /// | |
251 | /// # fn foo() -> std::io::Result<()> { | |
252 | /// let mut f = try!(File::create("foo.txt")); | |
253 | /// try!(f.write_all(b"Hello, world!")); | |
254 | /// | |
255 | /// try!(f.sync_data()); | |
256 | /// # Ok(()) | |
257 | /// # } | |
258 | /// ``` | |
259 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
260 | pub fn sync_data(&self) -> io::Result<()> { |
261 | self.inner.datasync() | |
262 | } | |
263 | ||
264 | /// Truncates or extends the underlying file, updating the size of | |
265 | /// this file to become `size`. | |
266 | /// | |
267 | /// If the `size` is less than the current file's size, then the file will | |
268 | /// be shrunk. If it is greater than the current file's size, then the file | |
269 | /// will be extended to `size` and have all of the intermediate data filled | |
270 | /// in with 0s. | |
c34b1796 AL |
271 | /// |
272 | /// # Examples | |
273 | /// | |
274 | /// ```no_run | |
275 | /// use std::fs::File; | |
276 | /// | |
277 | /// # fn foo() -> std::io::Result<()> { | |
278 | /// let mut f = try!(File::open("foo.txt")); | |
279 | /// try!(f.set_len(0)); | |
280 | /// # Ok(()) | |
281 | /// # } | |
282 | /// ``` | |
283 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
284 | pub fn set_len(&self, size: u64) -> io::Result<()> { |
285 | self.inner.truncate(size) | |
286 | } | |
287 | ||
c34b1796 AL |
288 | /// Queries metadata about the underlying file. |
289 | /// | |
290 | /// # Examples | |
291 | /// | |
292 | /// ```no_run | |
293 | /// use std::fs::File; | |
294 | /// | |
295 | /// # fn foo() -> std::io::Result<()> { | |
296 | /// let mut f = try!(File::open("foo.txt")); | |
297 | /// let metadata = try!(f.metadata()); | |
298 | /// # Ok(()) | |
299 | /// # } | |
300 | /// ``` | |
301 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
302 | pub fn metadata(&self) -> io::Result<Metadata> { |
303 | self.inner.file_attr().map(Metadata) | |
304 | } | |
305 | } | |
306 | ||
307 | impl AsInner<fs_imp::File> for File { | |
308 | fn as_inner(&self) -> &fs_imp::File { &self.inner } | |
309 | } | |
c34b1796 AL |
310 | impl FromInner<fs_imp::File> for File { |
311 | fn from_inner(f: fs_imp::File) -> File { | |
d9579d0f AL |
312 | File { inner: f } |
313 | } | |
314 | } | |
315 | ||
316 | impl fmt::Debug for File { | |
317 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
318 | self.inner.fmt(f) | |
c34b1796 AL |
319 | } |
320 | } | |
321 | ||
322 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
323 | impl Read for File { |
324 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
325 | self.inner.read(buf) | |
326 | } | |
327 | } | |
c34b1796 | 328 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
329 | impl Write for File { |
330 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
331 | self.inner.write(buf) | |
332 | } | |
333 | fn flush(&mut self) -> io::Result<()> { self.inner.flush() } | |
334 | } | |
c34b1796 | 335 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
336 | impl Seek for File { |
337 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { | |
338 | self.inner.seek(pos) | |
339 | } | |
340 | } | |
c34b1796 | 341 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
342 | impl<'a> Read for &'a File { |
343 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
344 | self.inner.read(buf) | |
345 | } | |
346 | } | |
c34b1796 | 347 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
348 | impl<'a> Write for &'a File { |
349 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
350 | self.inner.write(buf) | |
351 | } | |
352 | fn flush(&mut self) -> io::Result<()> { self.inner.flush() } | |
353 | } | |
c34b1796 | 354 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
355 | impl<'a> Seek for &'a File { |
356 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { | |
357 | self.inner.seek(pos) | |
358 | } | |
359 | } | |
360 | ||
361 | impl OpenOptions { | |
362 | /// Creates a blank net set of options ready for configuration. | |
363 | /// | |
364 | /// All options are initially set to `false`. | |
9346a6ac AL |
365 | /// |
366 | /// # Examples | |
367 | /// | |
368 | /// ```no_run | |
369 | /// use std::fs::OpenOptions; | |
370 | /// | |
371 | /// let file = OpenOptions::new().open("foo.txt"); | |
372 | /// ``` | |
c34b1796 | 373 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
374 | pub fn new() -> OpenOptions { |
375 | OpenOptions(fs_imp::OpenOptions::new()) | |
376 | } | |
377 | ||
9346a6ac | 378 | /// Sets the option for read access. |
85aaf69f SL |
379 | /// |
380 | /// This option, when true, will indicate that the file should be | |
381 | /// `read`-able if opened. | |
9346a6ac AL |
382 | /// |
383 | /// # Examples | |
384 | /// | |
385 | /// ```no_run | |
386 | /// use std::fs::OpenOptions; | |
387 | /// | |
388 | /// let file = OpenOptions::new().read(true).open("foo.txt"); | |
389 | /// ``` | |
c34b1796 | 390 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
391 | pub fn read(&mut self, read: bool) -> &mut OpenOptions { |
392 | self.0.read(read); self | |
393 | } | |
394 | ||
9346a6ac | 395 | /// Sets the option for write access. |
85aaf69f SL |
396 | /// |
397 | /// This option, when true, will indicate that the file should be | |
398 | /// `write`-able if opened. | |
9346a6ac AL |
399 | /// |
400 | /// # Examples | |
401 | /// | |
402 | /// ```no_run | |
403 | /// use std::fs::OpenOptions; | |
404 | /// | |
405 | /// let file = OpenOptions::new().write(true).open("foo.txt"); | |
406 | /// ``` | |
c34b1796 | 407 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
408 | pub fn write(&mut self, write: bool) -> &mut OpenOptions { |
409 | self.0.write(write); self | |
410 | } | |
411 | ||
9346a6ac | 412 | /// Sets the option for the append mode. |
85aaf69f SL |
413 | /// |
414 | /// This option, when true, means that writes will append to a file instead | |
415 | /// of overwriting previous contents. | |
9346a6ac AL |
416 | /// |
417 | /// # Examples | |
418 | /// | |
419 | /// ```no_run | |
420 | /// use std::fs::OpenOptions; | |
421 | /// | |
422 | /// let file = OpenOptions::new().append(true).open("foo.txt"); | |
423 | /// ``` | |
c34b1796 | 424 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
425 | pub fn append(&mut self, append: bool) -> &mut OpenOptions { |
426 | self.0.append(append); self | |
427 | } | |
428 | ||
9346a6ac | 429 | /// Sets the option for truncating a previous file. |
85aaf69f SL |
430 | /// |
431 | /// If a file is successfully opened with this option set it will truncate | |
432 | /// the file to 0 length if it already exists. | |
9346a6ac AL |
433 | /// |
434 | /// # Examples | |
435 | /// | |
436 | /// ```no_run | |
437 | /// use std::fs::OpenOptions; | |
438 | /// | |
439 | /// let file = OpenOptions::new().truncate(true).open("foo.txt"); | |
440 | /// ``` | |
c34b1796 | 441 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
442 | pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { |
443 | self.0.truncate(truncate); self | |
444 | } | |
445 | ||
9346a6ac | 446 | /// Sets the option for creating a new file. |
85aaf69f SL |
447 | /// |
448 | /// This option indicates whether a new file will be created if the file | |
449 | /// does not yet already exist. | |
9346a6ac AL |
450 | /// |
451 | /// # Examples | |
452 | /// | |
453 | /// ```no_run | |
454 | /// use std::fs::OpenOptions; | |
455 | /// | |
456 | /// let file = OpenOptions::new().create(true).open("foo.txt"); | |
457 | /// ``` | |
c34b1796 | 458 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
459 | pub fn create(&mut self, create: bool) -> &mut OpenOptions { |
460 | self.0.create(create); self | |
461 | } | |
462 | ||
9346a6ac | 463 | /// Opens a file at `path` with the options specified by `self`. |
85aaf69f SL |
464 | /// |
465 | /// # Errors | |
466 | /// | |
467 | /// This function will return an error under a number of different | |
468 | /// circumstances, to include but not limited to: | |
469 | /// | |
470 | /// * Opening a file that does not exist with read access. | |
471 | /// * Attempting to open a file with access that the user lacks | |
472 | /// permissions for | |
473 | /// * Filesystem-level errors (full disk, etc) | |
9346a6ac AL |
474 | /// |
475 | /// # Examples | |
476 | /// | |
477 | /// ```no_run | |
478 | /// use std::fs::OpenOptions; | |
479 | /// | |
480 | /// let file = OpenOptions::new().open("foo.txt"); | |
481 | /// ``` | |
c34b1796 AL |
482 | #[stable(feature = "rust1", since = "1.0.0")] |
483 | pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> { | |
484 | let path = path.as_ref(); | |
85aaf69f | 485 | let inner = try!(fs_imp::File::open(path, &self.0)); |
d9579d0f | 486 | Ok(File { inner: inner }) |
85aaf69f SL |
487 | } |
488 | } | |
c34b1796 | 489 | |
85aaf69f SL |
490 | impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { |
491 | fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } | |
492 | } | |
493 | ||
494 | impl Metadata { | |
d9579d0f AL |
495 | /// Returns the file type for this metadata. |
496 | #[stable(feature = "file_type", since = "1.1.0")] | |
497 | pub fn file_type(&self) -> FileType { | |
498 | FileType(self.0.file_type()) | |
499 | } | |
500 | ||
85aaf69f | 501 | /// Returns whether this metadata is for a directory. |
9346a6ac AL |
502 | /// |
503 | /// # Examples | |
504 | /// | |
505 | /// ``` | |
506 | /// # fn foo() -> std::io::Result<()> { | |
507 | /// use std::fs; | |
508 | /// | |
509 | /// let metadata = try!(fs::metadata("foo.txt")); | |
510 | /// | |
511 | /// assert!(!metadata.is_dir()); | |
512 | /// # Ok(()) | |
513 | /// # } | |
514 | /// ``` | |
c34b1796 | 515 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 516 | pub fn is_dir(&self) -> bool { self.file_type().is_dir() } |
85aaf69f SL |
517 | |
518 | /// Returns whether this metadata is for a regular file. | |
9346a6ac AL |
519 | /// |
520 | /// # Examples | |
521 | /// | |
522 | /// ``` | |
523 | /// # fn foo() -> std::io::Result<()> { | |
524 | /// use std::fs; | |
525 | /// | |
526 | /// let metadata = try!(fs::metadata("foo.txt")); | |
527 | /// | |
528 | /// assert!(metadata.is_file()); | |
529 | /// # Ok(()) | |
530 | /// # } | |
531 | /// ``` | |
c34b1796 | 532 | #[stable(feature = "rust1", since = "1.0.0")] |
d9579d0f | 533 | pub fn is_file(&self) -> bool { self.file_type().is_file() } |
85aaf69f SL |
534 | |
535 | /// Returns the size of the file, in bytes, this metadata is for. | |
9346a6ac AL |
536 | /// |
537 | /// # Examples | |
538 | /// | |
539 | /// ``` | |
540 | /// # fn foo() -> std::io::Result<()> { | |
541 | /// use std::fs; | |
542 | /// | |
543 | /// let metadata = try!(fs::metadata("foo.txt")); | |
544 | /// | |
545 | /// assert_eq!(0, metadata.len()); | |
546 | /// # Ok(()) | |
547 | /// # } | |
548 | /// ``` | |
c34b1796 | 549 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
550 | pub fn len(&self) -> u64 { self.0.size() } |
551 | ||
552 | /// Returns the permissions of the file this metadata is for. | |
9346a6ac AL |
553 | /// |
554 | /// # Examples | |
555 | /// | |
556 | /// ``` | |
557 | /// # fn foo() -> std::io::Result<()> { | |
558 | /// use std::fs; | |
559 | /// | |
560 | /// let metadata = try!(fs::metadata("foo.txt")); | |
561 | /// | |
562 | /// assert!(!metadata.permissions().readonly()); | |
563 | /// # Ok(()) | |
564 | /// # } | |
565 | /// ``` | |
c34b1796 | 566 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
567 | pub fn permissions(&self) -> Permissions { |
568 | Permissions(self.0.perm()) | |
569 | } | |
d9579d0f | 570 | } |
85aaf69f | 571 | |
d9579d0f AL |
572 | impl AsInner<fs_imp::FileAttr> for Metadata { |
573 | fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } | |
85aaf69f SL |
574 | } |
575 | ||
576 | impl Permissions { | |
577 | /// Returns whether these permissions describe a readonly file. | |
9346a6ac AL |
578 | /// |
579 | /// # Examples | |
580 | /// | |
581 | /// ``` | |
582 | /// use std::fs::File; | |
583 | /// | |
584 | /// # fn foo() -> std::io::Result<()> { | |
585 | /// let mut f = try!(File::create("foo.txt")); | |
586 | /// let metadata = try!(f.metadata()); | |
587 | /// | |
588 | /// assert_eq!(false, metadata.permissions().readonly()); | |
589 | /// # Ok(()) | |
590 | /// # } | |
591 | /// ``` | |
c34b1796 | 592 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
593 | pub fn readonly(&self) -> bool { self.0.readonly() } |
594 | ||
9346a6ac | 595 | /// Modifies the readonly flag for this set of permissions. |
85aaf69f SL |
596 | /// |
597 | /// This operation does **not** modify the filesystem. To modify the | |
598 | /// filesystem use the `fs::set_permissions` function. | |
9346a6ac AL |
599 | /// |
600 | /// # Examples | |
601 | /// | |
602 | /// ``` | |
603 | /// use std::fs::File; | |
604 | /// | |
605 | /// # fn foo() -> std::io::Result<()> { | |
d9579d0f | 606 | /// let f = try!(File::create("foo.txt")); |
9346a6ac AL |
607 | /// let metadata = try!(f.metadata()); |
608 | /// let mut permissions = metadata.permissions(); | |
609 | /// | |
610 | /// permissions.set_readonly(true); | |
611 | /// | |
612 | /// // filesystem doesn't change | |
613 | /// assert_eq!(false, metadata.permissions().readonly()); | |
614 | /// | |
615 | /// // just this particular `permissions`. | |
616 | /// assert_eq!(true, permissions.readonly()); | |
617 | /// # Ok(()) | |
618 | /// # } | |
619 | /// ``` | |
c34b1796 | 620 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
621 | pub fn set_readonly(&mut self, readonly: bool) { |
622 | self.0.set_readonly(readonly) | |
623 | } | |
624 | } | |
625 | ||
d9579d0f AL |
626 | impl FileType { |
627 | /// Test whether this file type represents a directory. | |
628 | #[stable(feature = "file_type", since = "1.1.0")] | |
629 | pub fn is_dir(&self) -> bool { self.0.is_dir() } | |
630 | ||
631 | /// Test whether this file type represents a regular file. | |
632 | #[stable(feature = "file_type", since = "1.1.0")] | |
633 | pub fn is_file(&self) -> bool { self.0.is_file() } | |
634 | ||
635 | /// Test whether this file type represents a symbolic link. | |
636 | #[stable(feature = "file_type", since = "1.1.0")] | |
637 | pub fn is_symlink(&self) -> bool { self.0.is_symlink() } | |
638 | } | |
639 | ||
85aaf69f SL |
640 | impl FromInner<fs_imp::FilePermissions> for Permissions { |
641 | fn from_inner(f: fs_imp::FilePermissions) -> Permissions { | |
642 | Permissions(f) | |
643 | } | |
644 | } | |
645 | ||
c34b1796 AL |
646 | impl AsInner<fs_imp::FilePermissions> for Permissions { |
647 | fn as_inner(&self) -> &fs_imp::FilePermissions { &self.0 } | |
648 | } | |
649 | ||
650 | #[stable(feature = "rust1", since = "1.0.0")] | |
85aaf69f SL |
651 | impl Iterator for ReadDir { |
652 | type Item = io::Result<DirEntry>; | |
653 | ||
654 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
655 | self.0.next().map(|entry| entry.map(DirEntry)) | |
656 | } | |
657 | } | |
658 | ||
c34b1796 | 659 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
660 | impl DirEntry { |
661 | /// Returns the full path to the file that this entry represents. | |
662 | /// | |
663 | /// The full path is created by joining the original path to `read_dir` or | |
664 | /// `walk_dir` with the filename of this entry. | |
9346a6ac AL |
665 | /// |
666 | /// # Examples | |
667 | /// | |
668 | /// ``` | |
669 | /// use std::fs; | |
670 | /// # fn foo() -> std::io::Result<()> { | |
671 | /// for entry in try!(fs::read_dir(".")) { | |
672 | /// let dir = try!(entry); | |
673 | /// println!("{:?}", dir.path()); | |
674 | /// } | |
675 | /// # Ok(()) | |
676 | /// # } | |
677 | /// ``` | |
678 | /// | |
679 | /// This prints output like: | |
680 | /// | |
681 | /// ```text | |
682 | /// "./whatever.txt" | |
683 | /// "./foo.html" | |
684 | /// "./hello_world.rs" | |
685 | /// ``` | |
686 | /// | |
687 | /// The exact text, of course, depends on what files you have in `.`. | |
c34b1796 | 688 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f | 689 | pub fn path(&self) -> PathBuf { self.0.path() } |
d9579d0f AL |
690 | |
691 | /// Return the metadata for the file that this entry points at. | |
692 | /// | |
693 | /// This function will not traverse symlinks if this entry points at a | |
694 | /// symlink. | |
695 | /// | |
696 | /// # Platform behavior | |
697 | /// | |
698 | /// On Windows this function is cheap to call (no extra system calls | |
699 | /// needed), but on Unix platforms this function is the equivalent of | |
700 | /// calling `symlink_metadata` on the path. | |
701 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
702 | pub fn metadata(&self) -> io::Result<Metadata> { | |
703 | self.0.metadata().map(Metadata) | |
704 | } | |
705 | ||
706 | /// Return the file type for the file that this entry points at. | |
707 | /// | |
708 | /// This function will not traverse symlinks if this entry points at a | |
709 | /// symlink. | |
710 | /// | |
711 | /// # Platform behavior | |
712 | /// | |
713 | /// On Windows and most Unix platforms this function is free (no extra | |
714 | /// system calls needed), but some Unix platforms may require the equivalent | |
715 | /// call to `symlink_metadata` to learn about the target file type. | |
716 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
717 | pub fn file_type(&self) -> io::Result<FileType> { | |
718 | self.0.file_type().map(FileType) | |
719 | } | |
720 | ||
721 | /// Returns the bare file name of this directory entry without any other | |
722 | /// leading path component. | |
723 | #[stable(feature = "dir_entry_ext", since = "1.1.0")] | |
724 | pub fn file_name(&self) -> OsString { | |
725 | self.0.file_name() | |
726 | } | |
727 | } | |
728 | ||
729 | impl AsInner<fs_imp::DirEntry> for DirEntry { | |
730 | fn as_inner(&self) -> &fs_imp::DirEntry { &self.0 } | |
85aaf69f SL |
731 | } |
732 | ||
9346a6ac | 733 | /// Removes a file from the underlying filesystem. |
85aaf69f SL |
734 | /// |
735 | /// Note that, just because an unlink call was successful, it is not | |
736 | /// guaranteed that a file is immediately deleted (e.g. depending on | |
737 | /// platform, other open file descriptors may prevent immediate removal). | |
738 | /// | |
739 | /// # Errors | |
740 | /// | |
741 | /// This function will return an error if `path` points to a directory, if the | |
742 | /// user lacks permissions to remove the file, or if some other filesystem-level | |
743 | /// error occurs. | |
9346a6ac AL |
744 | /// |
745 | /// # Examples | |
746 | /// | |
747 | /// ``` | |
748 | /// use std::fs; | |
749 | /// | |
750 | /// # fn foo() -> std::io::Result<()> { | |
751 | /// try!(fs::remove_file("a.txt")); | |
752 | /// # Ok(()) | |
753 | /// # } | |
754 | /// ``` | |
c34b1796 AL |
755 | #[stable(feature = "rust1", since = "1.0.0")] |
756 | pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
757 | fs_imp::unlink(path.as_ref()) | |
85aaf69f SL |
758 | } |
759 | ||
760 | /// Given a path, query the file system to get information about a file, | |
761 | /// directory, etc. | |
762 | /// | |
d9579d0f | 763 | /// This function will traverse symbolic links to query information about the |
85aaf69f SL |
764 | /// destination file. |
765 | /// | |
c34b1796 | 766 | /// # Examples |
85aaf69f | 767 | /// |
9346a6ac | 768 | /// ```rust |
85aaf69f SL |
769 | /// # fn foo() -> std::io::Result<()> { |
770 | /// use std::fs; | |
771 | /// | |
772 | /// let attr = try!(fs::metadata("/some/file/path.txt")); | |
773 | /// // inspect attr ... | |
774 | /// # Ok(()) | |
775 | /// # } | |
776 | /// ``` | |
777 | /// | |
778 | /// # Errors | |
779 | /// | |
780 | /// This function will return an error if the user lacks the requisite | |
781 | /// permissions to perform a `metadata` call on the given `path` or if there | |
782 | /// is no entry in the filesystem at the provided path. | |
c34b1796 AL |
783 | #[stable(feature = "rust1", since = "1.0.0")] |
784 | pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { | |
785 | fs_imp::stat(path.as_ref()).map(Metadata) | |
85aaf69f SL |
786 | } |
787 | ||
d9579d0f AL |
788 | /// Query the metadata about a file without following symlinks. |
789 | /// | |
790 | /// # Examples | |
791 | /// | |
792 | /// ```rust | |
793 | /// # fn foo() -> std::io::Result<()> { | |
794 | /// use std::fs; | |
795 | /// | |
796 | /// let attr = try!(fs::symlink_metadata("/some/file/path.txt")); | |
797 | /// // inspect attr ... | |
798 | /// # Ok(()) | |
799 | /// # } | |
800 | /// ``` | |
801 | #[stable(feature = "symlink_metadata", since = "1.1.0")] | |
802 | pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { | |
803 | fs_imp::lstat(path.as_ref()).map(Metadata) | |
804 | } | |
805 | ||
85aaf69f SL |
806 | /// Rename a file or directory to a new name. |
807 | /// | |
85aaf69f SL |
808 | /// # Errors |
809 | /// | |
810 | /// This function will return an error if the provided `from` doesn't exist, if | |
811 | /// the process lacks permissions to view the contents, if `from` and `to` | |
812 | /// reside on separate filesystems, or if some other intermittent I/O error | |
813 | /// occurs. | |
9346a6ac AL |
814 | /// |
815 | /// # Examples | |
816 | /// | |
817 | /// ``` | |
818 | /// use std::fs; | |
819 | /// | |
820 | /// # fn foo() -> std::io::Result<()> { | |
821 | /// try!(fs::rename("a.txt", "b.txt")); | |
822 | /// # Ok(()) | |
823 | /// # } | |
824 | /// ``` | |
c34b1796 AL |
825 | #[stable(feature = "rust1", since = "1.0.0")] |
826 | pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { | |
827 | fs_imp::rename(from.as_ref(), to.as_ref()) | |
85aaf69f SL |
828 | } |
829 | ||
830 | /// Copies the contents of one file to another. This function will also | |
831 | /// copy the permission bits of the original file to the destination file. | |
832 | /// | |
833 | /// This function will **overwrite** the contents of `to`. | |
834 | /// | |
835 | /// Note that if `from` and `to` both point to the same file, then the file | |
836 | /// will likely get truncated by this operation. | |
837 | /// | |
85aaf69f SL |
838 | /// # Errors |
839 | /// | |
840 | /// This function will return an error in the following situations, but is not | |
841 | /// limited to just these cases: | |
842 | /// | |
843 | /// * The `from` path is not a file | |
844 | /// * The `from` file does not exist | |
845 | /// * The current process does not have the permission rights to access | |
846 | /// `from` or write `to` | |
9346a6ac AL |
847 | /// |
848 | /// # Examples | |
849 | /// | |
850 | /// ```no_run | |
851 | /// use std::fs; | |
852 | /// | |
853 | /// # fn foo() -> std::io::Result<()> { | |
854 | /// try!(fs::copy("foo.txt", "bar.txt")); | |
855 | /// # Ok(()) } | |
856 | /// ``` | |
c34b1796 AL |
857 | #[stable(feature = "rust1", since = "1.0.0")] |
858 | pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { | |
859 | let from = from.as_ref(); | |
860 | let to = to.as_ref(); | |
85aaf69f | 861 | if !from.is_file() { |
c34b1796 AL |
862 | return Err(Error::new(ErrorKind::InvalidInput, |
863 | "the source path is not an existing file")) | |
85aaf69f SL |
864 | } |
865 | ||
866 | let mut reader = try!(File::open(from)); | |
867 | let mut writer = try!(File::create(to)); | |
868 | let perm = try!(reader.metadata()).permissions(); | |
869 | ||
870 | let ret = try!(io::copy(&mut reader, &mut writer)); | |
871 | try!(set_permissions(to, perm)); | |
872 | Ok(ret) | |
873 | } | |
874 | ||
875 | /// Creates a new hard link on the filesystem. | |
876 | /// | |
877 | /// The `dst` path will be a link pointing to the `src` path. Note that systems | |
878 | /// often require these two paths to both be located on the same filesystem. | |
9346a6ac AL |
879 | /// |
880 | /// # Examples | |
881 | /// | |
882 | /// ``` | |
883 | /// use std::fs; | |
884 | /// | |
885 | /// # fn foo() -> std::io::Result<()> { | |
886 | /// try!(fs::hard_link("a.txt", "b.txt")); | |
887 | /// # Ok(()) | |
888 | /// # } | |
889 | /// ``` | |
c34b1796 AL |
890 | #[stable(feature = "rust1", since = "1.0.0")] |
891 | pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { | |
892 | fs_imp::link(src.as_ref(), dst.as_ref()) | |
85aaf69f SL |
893 | } |
894 | ||
d9579d0f | 895 | /// Creates a new symbolic link on the filesystem. |
85aaf69f | 896 | /// |
d9579d0f AL |
897 | /// The `dst` path will be a symbolic link pointing to the `src` path. |
898 | /// On Windows, this will be a file symlink, not a directory symlink; | |
899 | /// for this reason, the platform-specific `std::os::unix::fs::symlink` | |
900 | /// and `std::os::windows::fs::{symlink_file, symlink_dir}` should be | |
901 | /// used instead to make the intent explicit. | |
9346a6ac AL |
902 | /// |
903 | /// # Examples | |
904 | /// | |
905 | /// ``` | |
906 | /// use std::fs; | |
907 | /// | |
908 | /// # fn foo() -> std::io::Result<()> { | |
909 | /// try!(fs::soft_link("a.txt", "b.txt")); | |
910 | /// # Ok(()) | |
911 | /// # } | |
912 | /// ``` | |
d9579d0f AL |
913 | #[deprecated(since = "1.0.0", |
914 | reason = "replaced with std::os::unix::fs::symlink and \ | |
915 | std::os::windows::fs::{symlink_file, symlink_dir}")] | |
c34b1796 AL |
916 | #[stable(feature = "rust1", since = "1.0.0")] |
917 | pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> { | |
918 | fs_imp::symlink(src.as_ref(), dst.as_ref()) | |
85aaf69f SL |
919 | } |
920 | ||
d9579d0f | 921 | /// Reads a symbolic link, returning the file that the link points to. |
85aaf69f SL |
922 | /// |
923 | /// # Errors | |
924 | /// | |
925 | /// This function will return an error on failure. Failure conditions include | |
d9579d0f | 926 | /// reading a file that does not exist or reading a file that is not a symbolic |
85aaf69f | 927 | /// link. |
85aaf69f | 928 | /// |
c34b1796 | 929 | /// # Examples |
85aaf69f | 930 | /// |
c34b1796 | 931 | /// ``` |
85aaf69f SL |
932 | /// use std::fs; |
933 | /// | |
9346a6ac AL |
934 | /// # fn foo() -> std::io::Result<()> { |
935 | /// let path = try!(fs::read_link("a.txt")); | |
936 | /// # Ok(()) | |
937 | /// # } | |
85aaf69f | 938 | /// ``` |
9346a6ac AL |
939 | #[stable(feature = "rust1", since = "1.0.0")] |
940 | pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { | |
941 | fs_imp::readlink(path.as_ref()) | |
942 | } | |
943 | ||
d9579d0f AL |
944 | /// Returns the canonical form of a path with all intermediate components |
945 | /// normalized and symbolic links resolved. | |
946 | #[unstable(feature = "fs_canonicalize", reason = "recently added API")] | |
947 | pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { | |
948 | fs_imp::canonicalize(path.as_ref()) | |
949 | } | |
950 | ||
9346a6ac | 951 | /// Creates a new, empty directory at the provided path |
85aaf69f SL |
952 | /// |
953 | /// # Errors | |
954 | /// | |
955 | /// This function will return an error if the user lacks permissions to make a | |
956 | /// new directory at the provided `path`, or if the directory already exists. | |
9346a6ac AL |
957 | /// |
958 | /// # Examples | |
959 | /// | |
960 | /// ``` | |
961 | /// use std::fs; | |
962 | /// | |
963 | /// # fn foo() -> std::io::Result<()> { | |
964 | /// try!(fs::create_dir("/some/dir")); | |
965 | /// # Ok(()) | |
966 | /// # } | |
967 | /// ``` | |
c34b1796 AL |
968 | #[stable(feature = "rust1", since = "1.0.0")] |
969 | pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
d9579d0f | 970 | DirBuilder::new().create(path.as_ref()) |
85aaf69f SL |
971 | } |
972 | ||
973 | /// Recursively create a directory and all of its parent components if they | |
974 | /// are missing. | |
975 | /// | |
976 | /// # Errors | |
977 | /// | |
978 | /// This function will fail if any directory in the path specified by `path` | |
979 | /// does not already exist and it could not be created otherwise. The specific | |
980 | /// error conditions for when a directory is being created (after it is | |
981 | /// determined to not exist) are outlined by `fs::create_dir`. | |
9346a6ac AL |
982 | /// |
983 | /// # Examples | |
984 | /// | |
985 | /// ``` | |
986 | /// use std::fs; | |
987 | /// | |
988 | /// # fn foo() -> std::io::Result<()> { | |
989 | /// try!(fs::create_dir_all("/some/dir")); | |
990 | /// # Ok(()) | |
991 | /// # } | |
992 | /// ``` | |
c34b1796 AL |
993 | #[stable(feature = "rust1", since = "1.0.0")] |
994 | pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
d9579d0f | 995 | DirBuilder::new().recursive(true).create(path.as_ref()) |
85aaf69f SL |
996 | } |
997 | ||
9346a6ac AL |
998 | /// Removes an existing, empty directory. |
999 | /// | |
1000 | /// # Errors | |
1001 | /// | |
1002 | /// This function will return an error if the user lacks permissions to remove | |
1003 | /// the directory at the provided `path`, or if the directory isn't empty. | |
85aaf69f | 1004 | /// |
c34b1796 | 1005 | /// # Examples |
85aaf69f | 1006 | /// |
c34b1796 | 1007 | /// ``` |
85aaf69f SL |
1008 | /// use std::fs; |
1009 | /// | |
9346a6ac AL |
1010 | /// # fn foo() -> std::io::Result<()> { |
1011 | /// try!(fs::remove_dir("/some/dir")); | |
1012 | /// # Ok(()) | |
1013 | /// # } | |
85aaf69f | 1014 | /// ``` |
c34b1796 AL |
1015 | #[stable(feature = "rust1", since = "1.0.0")] |
1016 | pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
1017 | fs_imp::rmdir(path.as_ref()) | |
85aaf69f SL |
1018 | } |
1019 | ||
1020 | /// Removes a directory at this path, after removing all its contents. Use | |
1021 | /// carefully! | |
1022 | /// | |
d9579d0f AL |
1023 | /// This function does **not** follow symbolic links and it will simply remove the |
1024 | /// symbolic link itself. | |
85aaf69f SL |
1025 | /// |
1026 | /// # Errors | |
1027 | /// | |
9346a6ac AL |
1028 | /// See `file::remove_file` and `fs::remove_dir`. |
1029 | /// | |
1030 | /// # Examples | |
1031 | /// | |
1032 | /// ``` | |
1033 | /// use std::fs; | |
1034 | /// | |
1035 | /// # fn foo() -> std::io::Result<()> { | |
1036 | /// try!(fs::remove_dir_all("/some/dir")); | |
1037 | /// # Ok(()) | |
1038 | /// # } | |
1039 | /// ``` | |
c34b1796 AL |
1040 | #[stable(feature = "rust1", since = "1.0.0")] |
1041 | pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
1042 | let path = path.as_ref(); | |
85aaf69f SL |
1043 | for child in try!(read_dir(path)) { |
1044 | let child = try!(child).path(); | |
d9579d0f | 1045 | let stat = try!(symlink_metadata(&*child)); |
85aaf69f SL |
1046 | if stat.is_dir() { |
1047 | try!(remove_dir_all(&*child)); | |
1048 | } else { | |
1049 | try!(remove_file(&*child)); | |
1050 | } | |
1051 | } | |
d9579d0f | 1052 | remove_dir(path) |
85aaf69f SL |
1053 | } |
1054 | ||
1055 | /// Returns an iterator over the entries within a directory. | |
1056 | /// | |
1057 | /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may | |
1058 | /// be encountered after an iterator is initially constructed. | |
1059 | /// | |
c34b1796 | 1060 | /// # Examples |
85aaf69f | 1061 | /// |
c34b1796 AL |
1062 | /// ``` |
1063 | /// # #![feature(path_ext)] | |
85aaf69f SL |
1064 | /// use std::io; |
1065 | /// use std::fs::{self, PathExt, DirEntry}; | |
1066 | /// use std::path::Path; | |
1067 | /// | |
1068 | /// // one possible implementation of fs::walk_dir only visiting files | |
1069 | /// fn visit_dirs(dir: &Path, cb: &mut FnMut(DirEntry)) -> io::Result<()> { | |
1070 | /// if dir.is_dir() { | |
1071 | /// for entry in try!(fs::read_dir(dir)) { | |
1072 | /// let entry = try!(entry); | |
1073 | /// if entry.path().is_dir() { | |
1074 | /// try!(visit_dirs(&entry.path(), cb)); | |
1075 | /// } else { | |
1076 | /// cb(entry); | |
1077 | /// } | |
1078 | /// } | |
1079 | /// } | |
1080 | /// Ok(()) | |
1081 | /// } | |
1082 | /// ``` | |
1083 | /// | |
1084 | /// # Errors | |
1085 | /// | |
1086 | /// This function will return an error if the provided `path` doesn't exist, if | |
1087 | /// the process lacks permissions to view the contents or if the `path` points | |
1088 | /// at a non-directory file | |
c34b1796 AL |
1089 | #[stable(feature = "rust1", since = "1.0.0")] |
1090 | pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { | |
1091 | fs_imp::readdir(path.as_ref()).map(ReadDir) | |
85aaf69f SL |
1092 | } |
1093 | ||
1094 | /// Returns an iterator that will recursively walk the directory structure | |
1095 | /// rooted at `path`. | |
1096 | /// | |
1097 | /// The path given will not be iterated over, and this will perform iteration in | |
1098 | /// some top-down order. The contents of unreadable subdirectories are ignored. | |
1099 | /// | |
1100 | /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may | |
1101 | /// be encountered after an iterator is initially constructed. | |
c34b1796 AL |
1102 | #[unstable(feature = "fs_walk", |
1103 | reason = "the precise semantics and defaults for a recursive walk \ | |
1104 | may change and this may end up accounting for files such \ | |
1105 | as symlinks differently")] | |
1106 | pub fn walk_dir<P: AsRef<Path>>(path: P) -> io::Result<WalkDir> { | |
85aaf69f SL |
1107 | let start = try!(read_dir(path)); |
1108 | Ok(WalkDir { cur: Some(start), stack: Vec::new() }) | |
1109 | } | |
1110 | ||
c34b1796 | 1111 | #[unstable(feature = "fs_walk")] |
85aaf69f SL |
1112 | impl Iterator for WalkDir { |
1113 | type Item = io::Result<DirEntry>; | |
1114 | ||
1115 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
1116 | loop { | |
1117 | if let Some(ref mut cur) = self.cur { | |
1118 | match cur.next() { | |
1119 | Some(Err(e)) => return Some(Err(e)), | |
1120 | Some(Ok(next)) => { | |
1121 | let path = next.path(); | |
1122 | if path.is_dir() { | |
1123 | self.stack.push(read_dir(&*path)); | |
1124 | } | |
1125 | return Some(Ok(next)) | |
1126 | } | |
1127 | None => {} | |
1128 | } | |
1129 | } | |
1130 | self.cur = None; | |
1131 | match self.stack.pop() { | |
1132 | Some(Err(e)) => return Some(Err(e)), | |
1133 | Some(Ok(next)) => self.cur = Some(next), | |
1134 | None => return None, | |
1135 | } | |
1136 | } | |
1137 | } | |
1138 | } | |
1139 | ||
1140 | /// Utility methods for paths. | |
c34b1796 AL |
1141 | #[unstable(feature = "path_ext", |
1142 | reason = "the precise set of methods exposed on this trait may \ | |
1143 | change and some methods may be removed")] | |
85aaf69f | 1144 | pub trait PathExt { |
9346a6ac | 1145 | /// Gets information on the file, directory, etc at this path. |
85aaf69f | 1146 | /// |
d9579d0f | 1147 | /// Consult the `fs::metadata` documentation for more info. |
85aaf69f | 1148 | /// |
d9579d0f AL |
1149 | /// This call preserves identical runtime/error semantics with |
1150 | /// `fs::metadata`. | |
85aaf69f SL |
1151 | fn metadata(&self) -> io::Result<Metadata>; |
1152 | ||
d9579d0f AL |
1153 | /// Gets information on the file, directory, etc at this path. |
1154 | /// | |
1155 | /// Consult the `fs::symlink_metadata` documentation for more info. | |
1156 | /// | |
1157 | /// This call preserves identical runtime/error semantics with | |
1158 | /// `fs::symlink_metadata`. | |
1159 | fn symlink_metadata(&self) -> io::Result<Metadata>; | |
1160 | ||
1161 | /// Returns the canonical form of a path, normalizing all components and | |
1162 | /// eliminate all symlinks. | |
1163 | /// | |
1164 | /// This call preserves identical runtime/error semantics with | |
1165 | /// `fs::canonicalize`. | |
1166 | fn canonicalize(&self) -> io::Result<PathBuf>; | |
1167 | ||
1168 | /// Reads the symlink at this path. | |
1169 | /// | |
1170 | /// For more information see `fs::read_link`. | |
1171 | fn read_link(&self) -> io::Result<PathBuf>; | |
1172 | ||
1173 | /// Reads the directory at this path. | |
1174 | /// | |
1175 | /// For more information see `fs::read_dir`. | |
1176 | fn read_dir(&self) -> io::Result<ReadDir>; | |
1177 | ||
85aaf69f SL |
1178 | /// Boolean value indicator whether the underlying file exists on the local |
1179 | /// filesystem. Returns false in exactly the cases where `fs::stat` fails. | |
1180 | fn exists(&self) -> bool; | |
1181 | ||
1182 | /// Whether the underlying implementation (be it a file path, or something | |
1183 | /// else) points at a "regular file" on the FS. Will return false for paths | |
1184 | /// to non-existent locations or directories or other non-regular files | |
1185 | /// (named pipes, etc). Follows links when making this determination. | |
1186 | fn is_file(&self) -> bool; | |
1187 | ||
1188 | /// Whether the underlying implementation (be it a file path, or something | |
1189 | /// else) is pointing at a directory in the underlying FS. Will return | |
1190 | /// false for paths to non-existent locations or if the item is not a | |
1191 | /// directory (eg files, named pipes, etc). Follows links when making this | |
1192 | /// determination. | |
1193 | fn is_dir(&self) -> bool; | |
1194 | } | |
1195 | ||
1196 | impl PathExt for Path { | |
1197 | fn metadata(&self) -> io::Result<Metadata> { metadata(self) } | |
d9579d0f AL |
1198 | fn symlink_metadata(&self) -> io::Result<Metadata> { symlink_metadata(self) } |
1199 | fn canonicalize(&self) -> io::Result<PathBuf> { canonicalize(self) } | |
1200 | fn read_link(&self) -> io::Result<PathBuf> { read_link(self) } | |
1201 | fn read_dir(&self) -> io::Result<ReadDir> { read_dir(self) } | |
85aaf69f SL |
1202 | fn exists(&self) -> bool { metadata(self).is_ok() } |
1203 | ||
1204 | fn is_file(&self) -> bool { | |
1205 | metadata(self).map(|s| s.is_file()).unwrap_or(false) | |
1206 | } | |
d9579d0f | 1207 | |
85aaf69f SL |
1208 | fn is_dir(&self) -> bool { |
1209 | metadata(self).map(|s| s.is_dir()).unwrap_or(false) | |
1210 | } | |
1211 | } | |
1212 | ||
1213 | /// Changes the timestamps for a file's last modification and access time. | |
1214 | /// | |
1215 | /// The file at the path specified will have its last access time set to | |
9346a6ac AL |
1216 | /// `accessed` and its modification time set to `modified`. The times specified |
1217 | /// should be in milliseconds. | |
c34b1796 AL |
1218 | #[unstable(feature = "fs_time", |
1219 | reason = "the argument type of u64 is not quite appropriate for \ | |
1220 | this function and may change if the standard library \ | |
1221 | gains a type to represent a moment in time")] | |
1222 | pub fn set_file_times<P: AsRef<Path>>(path: P, accessed: u64, | |
1223 | modified: u64) -> io::Result<()> { | |
1224 | fs_imp::utimes(path.as_ref(), accessed, modified) | |
85aaf69f SL |
1225 | } |
1226 | ||
1227 | /// Changes the permissions found on a file or a directory. | |
1228 | /// | |
c34b1796 | 1229 | /// # Examples |
85aaf69f SL |
1230 | /// |
1231 | /// ``` | |
1232 | /// # fn foo() -> std::io::Result<()> { | |
1233 | /// use std::fs; | |
1234 | /// | |
1235 | /// let mut perms = try!(fs::metadata("foo.txt")).permissions(); | |
1236 | /// perms.set_readonly(true); | |
1237 | /// try!(fs::set_permissions("foo.txt", perms)); | |
1238 | /// # Ok(()) | |
1239 | /// # } | |
1240 | /// ``` | |
1241 | /// | |
1242 | /// # Errors | |
1243 | /// | |
1244 | /// This function will return an error if the provided `path` doesn't exist, if | |
1245 | /// the process lacks permissions to change the attributes of the file, or if | |
1246 | /// some other I/O error is encountered. | |
d9579d0f AL |
1247 | #[stable(feature = "set_permissions", since = "1.1.0")] |
1248 | pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) | |
1249 | -> io::Result<()> { | |
c34b1796 | 1250 | fs_imp::set_perm(path.as_ref(), perm.0) |
85aaf69f SL |
1251 | } |
1252 | ||
d9579d0f AL |
1253 | #[unstable(feature = "dir_builder", reason = "recently added API")] |
1254 | impl DirBuilder { | |
1255 | /// Creates a new set of options with default mode/security settings for all | |
1256 | /// platforms and also non-recursive. | |
1257 | pub fn new() -> DirBuilder { | |
1258 | DirBuilder { | |
1259 | inner: fs_imp::DirBuilder::new(), | |
1260 | recursive: false, | |
1261 | } | |
1262 | } | |
1263 | ||
1264 | /// Indicate that directories create should be created recursively, creating | |
1265 | /// all parent directories if they do not exist with the same security and | |
1266 | /// permissions settings. | |
1267 | /// | |
1268 | /// This option defaults to `false` | |
1269 | pub fn recursive(&mut self, recursive: bool) -> &mut Self { | |
1270 | self.recursive = recursive; | |
1271 | self | |
1272 | } | |
1273 | ||
1274 | /// Create the specified directory with the options configured in this | |
1275 | /// builder. | |
1276 | pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { | |
1277 | let path = path.as_ref(); | |
1278 | if self.recursive { | |
1279 | self.create_dir_all(path) | |
1280 | } else { | |
1281 | self.inner.mkdir(path) | |
1282 | } | |
1283 | } | |
1284 | ||
1285 | fn create_dir_all(&self, path: &Path) -> io::Result<()> { | |
1286 | if path == Path::new("") || path.is_dir() { return Ok(()) } | |
1287 | if let Some(p) = path.parent() { | |
1288 | try!(self.create_dir_all(p)) | |
1289 | } | |
1290 | self.inner.mkdir(path) | |
1291 | } | |
1292 | } | |
1293 | ||
1294 | impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder { | |
1295 | fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { | |
1296 | &mut self.inner | |
1297 | } | |
1298 | } | |
1299 | ||
85aaf69f SL |
1300 | #[cfg(test)] |
1301 | mod tests { | |
c34b1796 AL |
1302 | #![allow(deprecated)] //rand |
1303 | ||
85aaf69f SL |
1304 | use prelude::v1::*; |
1305 | use io::prelude::*; | |
1306 | ||
c34b1796 | 1307 | use env; |
85aaf69f SL |
1308 | use fs::{self, File, OpenOptions}; |
1309 | use io::{ErrorKind, SeekFrom}; | |
1310 | use path::PathBuf; | |
1311 | use path::Path as Path2; | |
1312 | use os; | |
1313 | use rand::{self, StdRng, Rng}; | |
1314 | use str; | |
1315 | ||
1316 | macro_rules! check { ($e:expr) => ( | |
1317 | match $e { | |
1318 | Ok(t) => t, | |
1319 | Err(e) => panic!("{} failed with: {}", stringify!($e), e), | |
1320 | } | |
1321 | ) } | |
1322 | ||
1323 | macro_rules! error { ($e:expr, $s:expr) => ( | |
1324 | match $e { | |
1325 | Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), | |
c34b1796 | 1326 | Err(ref err) => assert!(err.to_string().contains($s), |
85aaf69f SL |
1327 | format!("`{}` did not contain `{}`", err, $s)) |
1328 | } | |
1329 | ) } | |
1330 | ||
1331 | pub struct TempDir(PathBuf); | |
1332 | ||
1333 | impl TempDir { | |
1334 | fn join(&self, path: &str) -> PathBuf { | |
1335 | let TempDir(ref p) = *self; | |
1336 | p.join(path) | |
1337 | } | |
1338 | ||
1339 | fn path<'a>(&'a self) -> &'a Path2 { | |
1340 | let TempDir(ref p) = *self; | |
1341 | p | |
1342 | } | |
1343 | } | |
1344 | ||
1345 | impl Drop for TempDir { | |
1346 | fn drop(&mut self) { | |
1347 | // Gee, seeing how we're testing the fs module I sure hope that we | |
1348 | // at least implement this correctly! | |
1349 | let TempDir(ref p) = *self; | |
1350 | check!(fs::remove_dir_all(p)); | |
1351 | } | |
1352 | } | |
1353 | ||
1354 | pub fn tmpdir() -> TempDir { | |
c34b1796 | 1355 | let p = env::temp_dir(); |
9346a6ac AL |
1356 | let mut r = rand::thread_rng(); |
1357 | let ret = p.join(&format!("rust-{}", r.next_u32())); | |
85aaf69f SL |
1358 | check!(fs::create_dir(&ret)); |
1359 | TempDir(ret) | |
1360 | } | |
1361 | ||
1362 | #[test] | |
1363 | fn file_test_io_smoke_test() { | |
1364 | let message = "it's alright. have a good time"; | |
1365 | let tmpdir = tmpdir(); | |
1366 | let filename = &tmpdir.join("file_rt_io_file_test.txt"); | |
1367 | { | |
1368 | let mut write_stream = check!(File::create(filename)); | |
1369 | check!(write_stream.write(message.as_bytes())); | |
1370 | } | |
1371 | { | |
1372 | let mut read_stream = check!(File::open(filename)); | |
1373 | let mut read_buf = [0; 1028]; | |
1374 | let read_str = match check!(read_stream.read(&mut read_buf)) { | |
c34b1796 | 1375 | 0 => panic!("shouldn't happen"), |
85aaf69f SL |
1376 | n => str::from_utf8(&read_buf[..n]).unwrap().to_string() |
1377 | }; | |
c34b1796 | 1378 | assert_eq!(read_str, message); |
85aaf69f SL |
1379 | } |
1380 | check!(fs::remove_file(filename)); | |
1381 | } | |
1382 | ||
1383 | #[test] | |
1384 | fn invalid_path_raises() { | |
1385 | let tmpdir = tmpdir(); | |
1386 | let filename = &tmpdir.join("file_that_does_not_exist.txt"); | |
1387 | let result = File::open(filename); | |
1388 | ||
1389 | if cfg!(unix) { | |
1390 | error!(result, "o such file or directory"); | |
1391 | } | |
1392 | // error!(result, "couldn't open path as file"); | |
1393 | // error!(result, format!("path={}; mode=open; access=read", filename.display())); | |
1394 | } | |
1395 | ||
1396 | #[test] | |
1397 | fn file_test_iounlinking_invalid_path_should_raise_condition() { | |
1398 | let tmpdir = tmpdir(); | |
1399 | let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); | |
1400 | ||
1401 | let result = fs::remove_file(filename); | |
1402 | ||
1403 | if cfg!(unix) { | |
1404 | error!(result, "o such file or directory"); | |
1405 | } | |
1406 | // error!(result, "couldn't unlink path"); | |
1407 | // error!(result, format!("path={}", filename.display())); | |
1408 | } | |
1409 | ||
1410 | #[test] | |
1411 | fn file_test_io_non_positional_read() { | |
1412 | let message: &str = "ten-four"; | |
1413 | let mut read_mem = [0; 8]; | |
1414 | let tmpdir = tmpdir(); | |
1415 | let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); | |
1416 | { | |
1417 | let mut rw_stream = check!(File::create(filename)); | |
1418 | check!(rw_stream.write(message.as_bytes())); | |
1419 | } | |
1420 | { | |
1421 | let mut read_stream = check!(File::open(filename)); | |
1422 | { | |
1423 | let read_buf = &mut read_mem[0..4]; | |
1424 | check!(read_stream.read(read_buf)); | |
1425 | } | |
1426 | { | |
1427 | let read_buf = &mut read_mem[4..8]; | |
1428 | check!(read_stream.read(read_buf)); | |
1429 | } | |
1430 | } | |
1431 | check!(fs::remove_file(filename)); | |
1432 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1433 | assert_eq!(read_str, message); | |
1434 | } | |
1435 | ||
1436 | #[test] | |
1437 | fn file_test_io_seek_and_tell_smoke_test() { | |
1438 | let message = "ten-four"; | |
1439 | let mut read_mem = [0; 4]; | |
1440 | let set_cursor = 4 as u64; | |
1441 | let mut tell_pos_pre_read; | |
1442 | let mut tell_pos_post_read; | |
1443 | let tmpdir = tmpdir(); | |
1444 | let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); | |
1445 | { | |
1446 | let mut rw_stream = check!(File::create(filename)); | |
1447 | check!(rw_stream.write(message.as_bytes())); | |
1448 | } | |
1449 | { | |
1450 | let mut read_stream = check!(File::open(filename)); | |
1451 | check!(read_stream.seek(SeekFrom::Start(set_cursor))); | |
1452 | tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
1453 | check!(read_stream.read(&mut read_mem)); | |
1454 | tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); | |
1455 | } | |
1456 | check!(fs::remove_file(filename)); | |
1457 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1458 | assert_eq!(read_str, &message[4..8]); | |
1459 | assert_eq!(tell_pos_pre_read, set_cursor); | |
1460 | assert_eq!(tell_pos_post_read, message.len() as u64); | |
1461 | } | |
1462 | ||
1463 | #[test] | |
1464 | fn file_test_io_seek_and_write() { | |
1465 | let initial_msg = "food-is-yummy"; | |
1466 | let overwrite_msg = "-the-bar!!"; | |
1467 | let final_msg = "foo-the-bar!!"; | |
1468 | let seek_idx = 3; | |
1469 | let mut read_mem = [0; 13]; | |
1470 | let tmpdir = tmpdir(); | |
1471 | let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); | |
1472 | { | |
1473 | let mut rw_stream = check!(File::create(filename)); | |
1474 | check!(rw_stream.write(initial_msg.as_bytes())); | |
1475 | check!(rw_stream.seek(SeekFrom::Start(seek_idx))); | |
1476 | check!(rw_stream.write(overwrite_msg.as_bytes())); | |
1477 | } | |
1478 | { | |
1479 | let mut read_stream = check!(File::open(filename)); | |
1480 | check!(read_stream.read(&mut read_mem)); | |
1481 | } | |
1482 | check!(fs::remove_file(filename)); | |
1483 | let read_str = str::from_utf8(&read_mem).unwrap(); | |
1484 | assert!(read_str == final_msg); | |
1485 | } | |
1486 | ||
1487 | #[test] | |
1488 | fn file_test_io_seek_shakedown() { | |
1489 | // 01234567890123 | |
1490 | let initial_msg = "qwer-asdf-zxcv"; | |
1491 | let chunk_one: &str = "qwer"; | |
1492 | let chunk_two: &str = "asdf"; | |
1493 | let chunk_three: &str = "zxcv"; | |
1494 | let mut read_mem = [0; 4]; | |
1495 | let tmpdir = tmpdir(); | |
1496 | let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); | |
1497 | { | |
1498 | let mut rw_stream = check!(File::create(filename)); | |
1499 | check!(rw_stream.write(initial_msg.as_bytes())); | |
1500 | } | |
1501 | { | |
1502 | let mut read_stream = check!(File::open(filename)); | |
1503 | ||
1504 | check!(read_stream.seek(SeekFrom::End(-4))); | |
1505 | check!(read_stream.read(&mut read_mem)); | |
1506 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); | |
1507 | ||
1508 | check!(read_stream.seek(SeekFrom::Current(-9))); | |
1509 | check!(read_stream.read(&mut read_mem)); | |
1510 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); | |
1511 | ||
1512 | check!(read_stream.seek(SeekFrom::Start(0))); | |
1513 | check!(read_stream.read(&mut read_mem)); | |
1514 | assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); | |
1515 | } | |
1516 | check!(fs::remove_file(filename)); | |
1517 | } | |
1518 | ||
1519 | #[test] | |
1520 | fn file_test_stat_is_correct_on_is_file() { | |
1521 | let tmpdir = tmpdir(); | |
1522 | let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); | |
1523 | { | |
1524 | let mut opts = OpenOptions::new(); | |
1525 | let mut fs = check!(opts.read(true).write(true) | |
1526 | .create(true).open(filename)); | |
1527 | let msg = "hw"; | |
1528 | fs.write(msg.as_bytes()).unwrap(); | |
1529 | ||
1530 | let fstat_res = check!(fs.metadata()); | |
1531 | assert!(fstat_res.is_file()); | |
1532 | } | |
1533 | let stat_res_fn = check!(fs::metadata(filename)); | |
1534 | assert!(stat_res_fn.is_file()); | |
1535 | let stat_res_meth = check!(filename.metadata()); | |
1536 | assert!(stat_res_meth.is_file()); | |
1537 | check!(fs::remove_file(filename)); | |
1538 | } | |
1539 | ||
1540 | #[test] | |
1541 | fn file_test_stat_is_correct_on_is_dir() { | |
1542 | let tmpdir = tmpdir(); | |
1543 | let filename = &tmpdir.join("file_stat_correct_on_is_dir"); | |
1544 | check!(fs::create_dir(filename)); | |
1545 | let stat_res_fn = check!(fs::metadata(filename)); | |
1546 | assert!(stat_res_fn.is_dir()); | |
1547 | let stat_res_meth = check!(filename.metadata()); | |
1548 | assert!(stat_res_meth.is_dir()); | |
1549 | check!(fs::remove_dir(filename)); | |
1550 | } | |
1551 | ||
1552 | #[test] | |
1553 | fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { | |
1554 | let tmpdir = tmpdir(); | |
1555 | let dir = &tmpdir.join("fileinfo_false_on_dir"); | |
1556 | check!(fs::create_dir(dir)); | |
1557 | assert!(dir.is_file() == false); | |
1558 | check!(fs::remove_dir(dir)); | |
1559 | } | |
1560 | ||
1561 | #[test] | |
1562 | fn file_test_fileinfo_check_exists_before_and_after_file_creation() { | |
1563 | let tmpdir = tmpdir(); | |
1564 | let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); | |
1565 | check!(check!(File::create(file)).write(b"foo")); | |
1566 | assert!(file.exists()); | |
1567 | check!(fs::remove_file(file)); | |
1568 | assert!(!file.exists()); | |
1569 | } | |
1570 | ||
1571 | #[test] | |
1572 | fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { | |
1573 | let tmpdir = tmpdir(); | |
1574 | let dir = &tmpdir.join("before_and_after_dir"); | |
1575 | assert!(!dir.exists()); | |
1576 | check!(fs::create_dir(dir)); | |
1577 | assert!(dir.exists()); | |
1578 | assert!(dir.is_dir()); | |
1579 | check!(fs::remove_dir(dir)); | |
1580 | assert!(!dir.exists()); | |
1581 | } | |
1582 | ||
1583 | #[test] | |
1584 | fn file_test_directoryinfo_readdir() { | |
1585 | let tmpdir = tmpdir(); | |
1586 | let dir = &tmpdir.join("di_readdir"); | |
1587 | check!(fs::create_dir(dir)); | |
1588 | let prefix = "foo"; | |
c34b1796 | 1589 | for n in 0..3 { |
85aaf69f SL |
1590 | let f = dir.join(&format!("{}.txt", n)); |
1591 | let mut w = check!(File::create(&f)); | |
1592 | let msg_str = format!("{}{}", prefix, n.to_string()); | |
1593 | let msg = msg_str.as_bytes(); | |
1594 | check!(w.write(msg)); | |
1595 | } | |
c34b1796 AL |
1596 | let files = check!(fs::read_dir(dir)); |
1597 | let mut mem = [0; 4]; | |
85aaf69f SL |
1598 | for f in files { |
1599 | let f = f.unwrap().path(); | |
1600 | { | |
1601 | let n = f.file_stem().unwrap(); | |
1602 | check!(check!(File::open(&f)).read(&mut mem)); | |
1603 | let read_str = str::from_utf8(&mem).unwrap(); | |
1604 | let expected = format!("{}{}", prefix, n.to_str().unwrap()); | |
c34b1796 | 1605 | assert_eq!(expected, read_str); |
85aaf69f SL |
1606 | } |
1607 | check!(fs::remove_file(&f)); | |
1608 | } | |
1609 | check!(fs::remove_dir(dir)); | |
1610 | } | |
1611 | ||
1612 | #[test] | |
1613 | fn file_test_walk_dir() { | |
1614 | let tmpdir = tmpdir(); | |
1615 | let dir = &tmpdir.join("walk_dir"); | |
1616 | check!(fs::create_dir(dir)); | |
1617 | ||
1618 | let dir1 = &dir.join("01/02/03"); | |
1619 | check!(fs::create_dir_all(dir1)); | |
1620 | check!(File::create(&dir1.join("04"))); | |
1621 | ||
1622 | let dir2 = &dir.join("11/12/13"); | |
1623 | check!(fs::create_dir_all(dir2)); | |
1624 | check!(File::create(&dir2.join("14"))); | |
1625 | ||
c34b1796 AL |
1626 | let files = check!(fs::walk_dir(dir)); |
1627 | let mut cur = [0; 2]; | |
85aaf69f SL |
1628 | for f in files { |
1629 | let f = f.unwrap().path(); | |
1630 | let stem = f.file_stem().unwrap().to_str().unwrap(); | |
1631 | let root = stem.as_bytes()[0] - b'0'; | |
1632 | let name = stem.as_bytes()[1] - b'0'; | |
1633 | assert!(cur[root as usize] < name); | |
1634 | cur[root as usize] = name; | |
1635 | } | |
1636 | ||
1637 | check!(fs::remove_dir_all(dir)); | |
1638 | } | |
1639 | ||
1640 | #[test] | |
1641 | fn mkdir_path_already_exists_error() { | |
1642 | let tmpdir = tmpdir(); | |
1643 | let dir = &tmpdir.join("mkdir_error_twice"); | |
1644 | check!(fs::create_dir(dir)); | |
1645 | let e = fs::create_dir(dir).err().unwrap(); | |
c34b1796 | 1646 | assert_eq!(e.kind(), ErrorKind::AlreadyExists); |
85aaf69f SL |
1647 | } |
1648 | ||
1649 | #[test] | |
1650 | fn recursive_mkdir() { | |
1651 | let tmpdir = tmpdir(); | |
1652 | let dir = tmpdir.join("d1/d2"); | |
1653 | check!(fs::create_dir_all(&dir)); | |
1654 | assert!(dir.is_dir()) | |
1655 | } | |
1656 | ||
1657 | #[test] | |
1658 | fn recursive_mkdir_failure() { | |
1659 | let tmpdir = tmpdir(); | |
1660 | let dir = tmpdir.join("d1"); | |
1661 | let file = dir.join("f1"); | |
1662 | ||
1663 | check!(fs::create_dir_all(&dir)); | |
1664 | check!(File::create(&file)); | |
1665 | ||
1666 | let result = fs::create_dir_all(&file); | |
1667 | ||
1668 | assert!(result.is_err()); | |
1669 | // error!(result, "couldn't recursively mkdir"); | |
1670 | // error!(result, "couldn't create directory"); | |
1671 | // error!(result, "mode=0700"); | |
1672 | // error!(result, format!("path={}", file.display())); | |
1673 | } | |
1674 | ||
1675 | #[test] | |
1676 | fn recursive_mkdir_slash() { | |
1677 | check!(fs::create_dir_all(&Path2::new("/"))); | |
1678 | } | |
1679 | ||
1680 | // FIXME(#12795) depends on lstat to work on windows | |
1681 | #[cfg(not(windows))] | |
1682 | #[test] | |
1683 | fn recursive_rmdir() { | |
1684 | let tmpdir = tmpdir(); | |
1685 | let d1 = tmpdir.join("d1"); | |
1686 | let dt = d1.join("t"); | |
1687 | let dtt = dt.join("t"); | |
1688 | let d2 = tmpdir.join("d2"); | |
1689 | let canary = d2.join("do_not_delete"); | |
1690 | check!(fs::create_dir_all(&dtt)); | |
1691 | check!(fs::create_dir_all(&d2)); | |
1692 | check!(check!(File::create(&canary)).write(b"foo")); | |
1693 | check!(fs::soft_link(&d2, &dt.join("d2"))); | |
1694 | check!(fs::remove_dir_all(&d1)); | |
1695 | ||
1696 | assert!(!d1.is_dir()); | |
1697 | assert!(canary.exists()); | |
1698 | } | |
1699 | ||
1700 | #[test] | |
1701 | fn unicode_path_is_dir() { | |
1702 | assert!(Path2::new(".").is_dir()); | |
1703 | assert!(!Path2::new("test/stdtest/fs.rs").is_dir()); | |
1704 | ||
1705 | let tmpdir = tmpdir(); | |
1706 | ||
1707 | let mut dirpath = tmpdir.path().to_path_buf(); | |
1708 | dirpath.push(&format!("test-가一ー你好")); | |
1709 | check!(fs::create_dir(&dirpath)); | |
1710 | assert!(dirpath.is_dir()); | |
1711 | ||
1712 | let mut filepath = dirpath; | |
1713 | filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); | |
1714 | check!(File::create(&filepath)); // ignore return; touch only | |
1715 | assert!(!filepath.is_dir()); | |
1716 | assert!(filepath.exists()); | |
1717 | } | |
1718 | ||
1719 | #[test] | |
1720 | fn unicode_path_exists() { | |
1721 | assert!(Path2::new(".").exists()); | |
1722 | assert!(!Path2::new("test/nonexistent-bogus-path").exists()); | |
1723 | ||
1724 | let tmpdir = tmpdir(); | |
1725 | let unicode = tmpdir.path(); | |
1726 | let unicode = unicode.join(&format!("test-각丁ー再见")); | |
1727 | check!(fs::create_dir(&unicode)); | |
1728 | assert!(unicode.exists()); | |
1729 | assert!(!Path2::new("test/unicode-bogus-path-각丁ー再见").exists()); | |
1730 | } | |
1731 | ||
1732 | #[test] | |
1733 | fn copy_file_does_not_exist() { | |
1734 | let from = Path2::new("test/nonexistent-bogus-path"); | |
1735 | let to = Path2::new("test/other-bogus-path"); | |
1736 | ||
1737 | match fs::copy(&from, &to) { | |
1738 | Ok(..) => panic!(), | |
1739 | Err(..) => { | |
1740 | assert!(!from.exists()); | |
1741 | assert!(!to.exists()); | |
1742 | } | |
1743 | } | |
1744 | } | |
1745 | ||
1746 | #[test] | |
1747 | fn copy_file_ok() { | |
1748 | let tmpdir = tmpdir(); | |
1749 | let input = tmpdir.join("in.txt"); | |
1750 | let out = tmpdir.join("out.txt"); | |
1751 | ||
1752 | check!(check!(File::create(&input)).write(b"hello")); | |
1753 | check!(fs::copy(&input, &out)); | |
1754 | let mut v = Vec::new(); | |
1755 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
c34b1796 | 1756 | assert_eq!(v, b"hello"); |
85aaf69f SL |
1757 | |
1758 | assert_eq!(check!(input.metadata()).permissions(), | |
1759 | check!(out.metadata()).permissions()); | |
1760 | } | |
1761 | ||
1762 | #[test] | |
1763 | fn copy_file_dst_dir() { | |
1764 | let tmpdir = tmpdir(); | |
1765 | let out = tmpdir.join("out"); | |
1766 | ||
1767 | check!(File::create(&out)); | |
1768 | match fs::copy(&*out, tmpdir.path()) { | |
1769 | Ok(..) => panic!(), Err(..) => {} | |
1770 | } | |
1771 | } | |
1772 | ||
1773 | #[test] | |
1774 | fn copy_file_dst_exists() { | |
1775 | let tmpdir = tmpdir(); | |
1776 | let input = tmpdir.join("in"); | |
1777 | let output = tmpdir.join("out"); | |
1778 | ||
1779 | check!(check!(File::create(&input)).write("foo".as_bytes())); | |
1780 | check!(check!(File::create(&output)).write("bar".as_bytes())); | |
1781 | check!(fs::copy(&input, &output)); | |
1782 | ||
1783 | let mut v = Vec::new(); | |
1784 | check!(check!(File::open(&output)).read_to_end(&mut v)); | |
1785 | assert_eq!(v, b"foo".to_vec()); | |
1786 | } | |
1787 | ||
1788 | #[test] | |
1789 | fn copy_file_src_dir() { | |
1790 | let tmpdir = tmpdir(); | |
1791 | let out = tmpdir.join("out"); | |
1792 | ||
1793 | match fs::copy(tmpdir.path(), &out) { | |
1794 | Ok(..) => panic!(), Err(..) => {} | |
1795 | } | |
1796 | assert!(!out.exists()); | |
1797 | } | |
1798 | ||
1799 | #[test] | |
1800 | fn copy_file_preserves_perm_bits() { | |
1801 | let tmpdir = tmpdir(); | |
1802 | let input = tmpdir.join("in.txt"); | |
1803 | let out = tmpdir.join("out.txt"); | |
1804 | ||
1805 | let attr = check!(check!(File::create(&input)).metadata()); | |
1806 | let mut p = attr.permissions(); | |
1807 | p.set_readonly(true); | |
1808 | check!(fs::set_permissions(&input, p)); | |
1809 | check!(fs::copy(&input, &out)); | |
1810 | assert!(check!(out.metadata()).permissions().readonly()); | |
c34b1796 AL |
1811 | check!(fs::set_permissions(&input, attr.permissions())); |
1812 | check!(fs::set_permissions(&out, attr.permissions())); | |
85aaf69f SL |
1813 | } |
1814 | ||
1815 | #[cfg(not(windows))] // FIXME(#10264) operation not permitted? | |
1816 | #[test] | |
1817 | fn symlinks_work() { | |
1818 | let tmpdir = tmpdir(); | |
1819 | let input = tmpdir.join("in.txt"); | |
1820 | let out = tmpdir.join("out.txt"); | |
1821 | ||
1822 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
1823 | check!(fs::soft_link(&input, &out)); | |
1824 | // if cfg!(not(windows)) { | |
1825 | // assert_eq!(check!(lstat(&out)).kind, FileType::Symlink); | |
1826 | // assert_eq!(check!(out.lstat()).kind, FileType::Symlink); | |
1827 | // } | |
1828 | assert_eq!(check!(fs::metadata(&out)).len(), | |
1829 | check!(fs::metadata(&input)).len()); | |
1830 | let mut v = Vec::new(); | |
1831 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
1832 | assert_eq!(v, b"foobar".to_vec()); | |
1833 | } | |
1834 | ||
1835 | #[cfg(not(windows))] // apparently windows doesn't like symlinks | |
1836 | #[test] | |
1837 | fn symlink_noexist() { | |
1838 | let tmpdir = tmpdir(); | |
1839 | // symlinks can point to things that don't exist | |
1840 | check!(fs::soft_link(&tmpdir.join("foo"), &tmpdir.join("bar"))); | |
1841 | assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))), | |
1842 | tmpdir.join("foo")); | |
1843 | } | |
1844 | ||
1845 | #[test] | |
1846 | fn readlink_not_symlink() { | |
1847 | let tmpdir = tmpdir(); | |
1848 | match fs::read_link(tmpdir.path()) { | |
1849 | Ok(..) => panic!("wanted a failure"), | |
1850 | Err(..) => {} | |
1851 | } | |
1852 | } | |
1853 | ||
1854 | #[test] | |
1855 | fn links_work() { | |
1856 | let tmpdir = tmpdir(); | |
1857 | let input = tmpdir.join("in.txt"); | |
1858 | let out = tmpdir.join("out.txt"); | |
1859 | ||
1860 | check!(check!(File::create(&input)).write("foobar".as_bytes())); | |
1861 | check!(fs::hard_link(&input, &out)); | |
1862 | assert_eq!(check!(fs::metadata(&out)).len(), | |
1863 | check!(fs::metadata(&input)).len()); | |
1864 | assert_eq!(check!(fs::metadata(&out)).len(), | |
1865 | check!(input.metadata()).len()); | |
1866 | let mut v = Vec::new(); | |
1867 | check!(check!(File::open(&out)).read_to_end(&mut v)); | |
1868 | assert_eq!(v, b"foobar".to_vec()); | |
1869 | ||
1870 | // can't link to yourself | |
1871 | match fs::hard_link(&input, &input) { | |
1872 | Ok(..) => panic!("wanted a failure"), | |
1873 | Err(..) => {} | |
1874 | } | |
1875 | // can't link to something that doesn't exist | |
1876 | match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { | |
1877 | Ok(..) => panic!("wanted a failure"), | |
1878 | Err(..) => {} | |
1879 | } | |
1880 | } | |
1881 | ||
1882 | #[test] | |
1883 | fn chmod_works() { | |
1884 | let tmpdir = tmpdir(); | |
1885 | let file = tmpdir.join("in.txt"); | |
1886 | ||
1887 | check!(File::create(&file)); | |
1888 | let attr = check!(fs::metadata(&file)); | |
1889 | assert!(!attr.permissions().readonly()); | |
1890 | let mut p = attr.permissions(); | |
1891 | p.set_readonly(true); | |
1892 | check!(fs::set_permissions(&file, p.clone())); | |
1893 | let attr = check!(fs::metadata(&file)); | |
1894 | assert!(attr.permissions().readonly()); | |
1895 | ||
c34b1796 AL |
1896 | match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { |
1897 | Ok(..) => panic!("wanted an error"), | |
85aaf69f SL |
1898 | Err(..) => {} |
1899 | } | |
c34b1796 AL |
1900 | |
1901 | p.set_readonly(false); | |
1902 | check!(fs::set_permissions(&file, p)); | |
85aaf69f SL |
1903 | } |
1904 | ||
1905 | #[test] | |
1906 | fn sync_doesnt_kill_anything() { | |
1907 | let tmpdir = tmpdir(); | |
1908 | let path = tmpdir.join("in.txt"); | |
1909 | ||
1910 | let mut file = check!(File::create(&path)); | |
1911 | check!(file.sync_all()); | |
1912 | check!(file.sync_data()); | |
1913 | check!(file.write(b"foo")); | |
1914 | check!(file.sync_all()); | |
1915 | check!(file.sync_data()); | |
1916 | } | |
1917 | ||
1918 | #[test] | |
1919 | fn truncate_works() { | |
1920 | let tmpdir = tmpdir(); | |
1921 | let path = tmpdir.join("in.txt"); | |
1922 | ||
1923 | let mut file = check!(File::create(&path)); | |
1924 | check!(file.write(b"foo")); | |
1925 | check!(file.sync_all()); | |
1926 | ||
1927 | // Do some simple things with truncation | |
1928 | assert_eq!(check!(file.metadata()).len(), 3); | |
1929 | check!(file.set_len(10)); | |
1930 | assert_eq!(check!(file.metadata()).len(), 10); | |
1931 | check!(file.write(b"bar")); | |
1932 | check!(file.sync_all()); | |
1933 | assert_eq!(check!(file.metadata()).len(), 10); | |
1934 | ||
1935 | let mut v = Vec::new(); | |
1936 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
1937 | assert_eq!(v, b"foobar\0\0\0\0".to_vec()); | |
1938 | ||
1939 | // Truncate to a smaller length, don't seek, and then write something. | |
c34b1796 | 1940 | // Ensure that the intermediate zeroes are all filled in (we have `seek`ed |
85aaf69f SL |
1941 | // past the end of the file). |
1942 | check!(file.set_len(2)); | |
1943 | assert_eq!(check!(file.metadata()).len(), 2); | |
1944 | check!(file.write(b"wut")); | |
1945 | check!(file.sync_all()); | |
1946 | assert_eq!(check!(file.metadata()).len(), 9); | |
1947 | let mut v = Vec::new(); | |
1948 | check!(check!(File::open(&path)).read_to_end(&mut v)); | |
1949 | assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); | |
1950 | } | |
1951 | ||
1952 | #[test] | |
1953 | fn open_flavors() { | |
1954 | use fs::OpenOptions as OO; | |
1955 | fn c<T: Clone>(t: &T) -> T { t.clone() } | |
1956 | ||
1957 | let tmpdir = tmpdir(); | |
1958 | ||
1959 | let mut r = OO::new(); r.read(true); | |
1960 | let mut w = OO::new(); w.write(true); | |
1961 | let mut rw = OO::new(); rw.write(true).read(true); | |
1962 | ||
1963 | match r.open(&tmpdir.join("a")) { | |
1964 | Ok(..) => panic!(), Err(..) => {} | |
1965 | } | |
1966 | ||
1967 | // Perform each one twice to make sure that it succeeds the second time | |
1968 | // (where the file exists) | |
1969 | check!(c(&w).create(true).open(&tmpdir.join("b"))); | |
1970 | assert!(tmpdir.join("b").exists()); | |
1971 | check!(c(&w).create(true).open(&tmpdir.join("b"))); | |
1972 | check!(w.open(&tmpdir.join("b"))); | |
1973 | ||
1974 | check!(c(&rw).create(true).open(&tmpdir.join("c"))); | |
1975 | assert!(tmpdir.join("c").exists()); | |
1976 | check!(c(&rw).create(true).open(&tmpdir.join("c"))); | |
1977 | check!(rw.open(&tmpdir.join("c"))); | |
1978 | ||
1979 | check!(c(&w).append(true).create(true).open(&tmpdir.join("d"))); | |
1980 | assert!(tmpdir.join("d").exists()); | |
1981 | check!(c(&w).append(true).create(true).open(&tmpdir.join("d"))); | |
1982 | check!(c(&w).append(true).open(&tmpdir.join("d"))); | |
1983 | ||
1984 | check!(c(&rw).append(true).create(true).open(&tmpdir.join("e"))); | |
1985 | assert!(tmpdir.join("e").exists()); | |
1986 | check!(c(&rw).append(true).create(true).open(&tmpdir.join("e"))); | |
1987 | check!(c(&rw).append(true).open(&tmpdir.join("e"))); | |
1988 | ||
1989 | check!(c(&w).truncate(true).create(true).open(&tmpdir.join("f"))); | |
1990 | assert!(tmpdir.join("f").exists()); | |
1991 | check!(c(&w).truncate(true).create(true).open(&tmpdir.join("f"))); | |
1992 | check!(c(&w).truncate(true).open(&tmpdir.join("f"))); | |
1993 | ||
1994 | check!(c(&rw).truncate(true).create(true).open(&tmpdir.join("g"))); | |
1995 | assert!(tmpdir.join("g").exists()); | |
1996 | check!(c(&rw).truncate(true).create(true).open(&tmpdir.join("g"))); | |
1997 | check!(c(&rw).truncate(true).open(&tmpdir.join("g"))); | |
1998 | ||
1999 | check!(check!(File::create(&tmpdir.join("h"))).write("foo".as_bytes())); | |
2000 | check!(r.open(&tmpdir.join("h"))); | |
2001 | { | |
2002 | let mut f = check!(r.open(&tmpdir.join("h"))); | |
2003 | assert!(f.write("wut".as_bytes()).is_err()); | |
2004 | } | |
2005 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); | |
2006 | { | |
2007 | let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); | |
2008 | check!(f.write("bar".as_bytes())); | |
2009 | } | |
2010 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); | |
2011 | { | |
2012 | let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); | |
2013 | check!(f.write("bar".as_bytes())); | |
2014 | } | |
2015 | assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); | |
2016 | } | |
2017 | ||
2018 | #[test] | |
2019 | fn utime() { | |
2020 | let tmpdir = tmpdir(); | |
2021 | let path = tmpdir.join("a"); | |
2022 | check!(File::create(&path)); | |
2023 | // These numbers have to be bigger than the time in the day to account | |
2024 | // for timezones Windows in particular will fail in certain timezones | |
2025 | // with small enough values | |
d9579d0f AL |
2026 | check!(fs::set_file_times(&path, 100_000, 200_000)); |
2027 | ||
2028 | check(&check!(path.metadata())); | |
2029 | ||
2030 | #[cfg(unix)] | |
2031 | fn check(metadata: &fs::Metadata) { | |
2032 | use os::unix::prelude::*; | |
2033 | assert_eq!(metadata.atime(), 100); | |
2034 | assert_eq!(metadata.atime_nsec(), 0); | |
2035 | assert_eq!(metadata.mtime(), 200); | |
2036 | assert_eq!(metadata.mtime_nsec(), 0); | |
2037 | } | |
2038 | #[cfg(windows)] | |
2039 | fn check(metadata: &fs::Metadata) { | |
2040 | use os::windows::prelude::*; | |
2041 | assert_eq!(metadata.last_access_time(), 100_000 * 10_000); | |
2042 | assert_eq!(metadata.last_write_time(), 200_000 * 10_000); | |
2043 | } | |
85aaf69f SL |
2044 | } |
2045 | ||
2046 | #[test] | |
2047 | fn utime_noexist() { | |
2048 | let tmpdir = tmpdir(); | |
2049 | ||
2050 | match fs::set_file_times(&tmpdir.join("a"), 100, 200) { | |
2051 | Ok(..) => panic!(), | |
2052 | Err(..) => {} | |
2053 | } | |
2054 | } | |
2055 | ||
2056 | #[test] | |
2057 | fn binary_file() { | |
2058 | let mut bytes = [0; 1024]; | |
c34b1796 | 2059 | StdRng::new().unwrap().fill_bytes(&mut bytes); |
85aaf69f SL |
2060 | |
2061 | let tmpdir = tmpdir(); | |
2062 | ||
2063 | check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); | |
2064 | let mut v = Vec::new(); | |
2065 | check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); | |
c34b1796 | 2066 | assert!(v == &bytes[..]); |
85aaf69f SL |
2067 | } |
2068 | ||
2069 | #[test] | |
c34b1796 | 2070 | #[cfg(not(windows))] |
85aaf69f SL |
2071 | fn unlink_readonly() { |
2072 | let tmpdir = tmpdir(); | |
2073 | let path = tmpdir.join("file"); | |
2074 | check!(File::create(&path)); | |
2075 | let mut perm = check!(fs::metadata(&path)).permissions(); | |
2076 | perm.set_readonly(true); | |
2077 | check!(fs::set_permissions(&path, perm)); | |
2078 | check!(fs::remove_file(&path)); | |
2079 | } | |
c34b1796 AL |
2080 | |
2081 | #[test] | |
2082 | fn mkdir_trailing_slash() { | |
2083 | let tmpdir = tmpdir(); | |
2084 | let path = tmpdir.join("file"); | |
2085 | check!(fs::create_dir_all(&path.join("a/"))); | |
2086 | } | |
d9579d0f AL |
2087 | |
2088 | #[test] | |
2089 | #[cfg(not(windows))] | |
2090 | fn realpath_works() { | |
2091 | let tmpdir = tmpdir(); | |
2092 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
2093 | let file = tmpdir.join("test"); | |
2094 | let dir = tmpdir.join("test2"); | |
2095 | let link = dir.join("link"); | |
2096 | let linkdir = tmpdir.join("test3"); | |
2097 | ||
2098 | File::create(&file).unwrap(); | |
2099 | fs::create_dir(&dir).unwrap(); | |
2100 | fs::soft_link(&file, &link).unwrap(); | |
2101 | fs::soft_link(&dir, &linkdir).unwrap(); | |
2102 | ||
2103 | assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); | |
2104 | ||
2105 | assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); | |
2106 | assert_eq!(fs::canonicalize(&file).unwrap(), file); | |
2107 | assert_eq!(fs::canonicalize(&link).unwrap(), file); | |
2108 | assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); | |
2109 | assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); | |
2110 | } | |
2111 | ||
2112 | #[test] | |
2113 | #[cfg(not(windows))] | |
2114 | fn realpath_works_tricky() { | |
2115 | let tmpdir = tmpdir(); | |
2116 | let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); | |
2117 | ||
2118 | let a = tmpdir.join("a"); | |
2119 | let b = a.join("b"); | |
2120 | let c = b.join("c"); | |
2121 | let d = a.join("d"); | |
2122 | let e = d.join("e"); | |
2123 | let f = a.join("f"); | |
2124 | ||
2125 | fs::create_dir_all(&b).unwrap(); | |
2126 | fs::create_dir_all(&d).unwrap(); | |
2127 | File::create(&f).unwrap(); | |
2128 | fs::soft_link("../d/e", &c).unwrap(); | |
2129 | fs::soft_link("../f", &e).unwrap(); | |
2130 | ||
2131 | assert_eq!(fs::canonicalize(&c).unwrap(), f); | |
2132 | assert_eq!(fs::canonicalize(&e).unwrap(), f); | |
2133 | } | |
2134 | ||
2135 | #[test] | |
2136 | fn dir_entry_methods() { | |
2137 | let tmpdir = tmpdir(); | |
2138 | ||
2139 | fs::create_dir_all(&tmpdir.join("a")).unwrap(); | |
2140 | File::create(&tmpdir.join("b")).unwrap(); | |
2141 | ||
2142 | for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { | |
2143 | let fname = file.file_name(); | |
2144 | match fname.to_str() { | |
2145 | Some("a") => { | |
2146 | assert!(file.file_type().unwrap().is_dir()); | |
2147 | assert!(file.metadata().unwrap().is_dir()); | |
2148 | } | |
2149 | Some("b") => { | |
2150 | assert!(file.file_type().unwrap().is_file()); | |
2151 | assert!(file.metadata().unwrap().is_file()); | |
2152 | } | |
2153 | f => panic!("unknown file name: {:?}", f), | |
2154 | } | |
2155 | } | |
2156 | } | |
85aaf69f | 2157 | } |