]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/tempfile.rs
prefer "FIXME" to "TODO".
[rust.git] / src / libstd / io / tempfile.rs
1 // Copyright 2013 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 //! Temporary files and directories
12
13 use io::{fs, IoResult};
14 use io;
15 use libc;
16 use ops::Drop;
17 use option::{Option, None, Some};
18 use os;
19 use path::{Path, GenericPath};
20 use result::{Ok, Err};
21 use sync::atomic;
22
23 /// A wrapper for a path to temporary directory implementing automatic
24 /// scope-based deletion.
25 pub struct TempDir {
26     path: Option<Path>,
27     disarmed: bool
28 }
29
30 impl TempDir {
31     /// Attempts to make a temporary directory inside of `tmpdir` whose name
32     /// will have the suffix `suffix`. The directory will be automatically
33     /// deleted once the returned wrapper is destroyed.
34     ///
35     /// If no directory can be created, `Err` is returned.
36     pub fn new_in(tmpdir: &Path, suffix: &str) -> IoResult<TempDir> {
37         if !tmpdir.is_absolute() {
38             let abs_tmpdir = try!(os::make_absolute(tmpdir));
39             return TempDir::new_in(&abs_tmpdir, suffix);
40         }
41
42         static CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT;
43
44         let mut attempts = 0u;
45         loop {
46             let filename =
47                 format!("rs-{}-{}-{}",
48                         unsafe { libc::getpid() },
49                         CNT.fetch_add(1, atomic::SeqCst),
50                         suffix);
51             let p = tmpdir.join(filename);
52             match fs::mkdir(&p, io::USER_RWX) {
53                 Err(error) => {
54                     if attempts >= 1000 {
55                         return Err(error)
56                     }
57                     attempts += 1;
58                 }
59                 Ok(()) => return Ok(TempDir { path: Some(p), disarmed: false })
60             }
61         }
62     }
63
64     /// Attempts to make a temporary directory inside of `os::tmpdir()` whose
65     /// name will have the suffix `suffix`. The directory will be automatically
66     /// deleted once the returned wrapper is destroyed.
67     ///
68     /// If no directory can be created, `Err` is returned.
69     pub fn new(suffix: &str) -> IoResult<TempDir> {
70         TempDir::new_in(&os::tmpdir(), suffix)
71     }
72
73     /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
74     /// This discards the wrapper so that the automatic deletion of the
75     /// temporary directory is prevented.
76     pub fn into_inner(self) -> Path {
77         let mut tmpdir = self;
78         tmpdir.path.take().unwrap()
79     }
80
81     /// Deprecated, use into_inner() instead
82     #[deprecated = "renamed to into_inner()"]
83     pub fn unwrap(self) -> Path { self.into_inner() }
84
85     /// Access the wrapped `std::path::Path` to the temporary directory.
86     pub fn path<'a>(&'a self) -> &'a Path {
87         self.path.as_ref().unwrap()
88     }
89
90     /// Close and remove the temporary directory
91     ///
92     /// Although `TempDir` removes the directory on drop, in the destructor
93     /// any errors are ignored. To detect errors cleaning up the temporary
94     /// directory, call `close` instead.
95     pub fn close(mut self) -> IoResult<()> {
96         self.cleanup_dir()
97     }
98
99     fn cleanup_dir(&mut self) -> IoResult<()> {
100         assert!(!self.disarmed);
101         self.disarmed = true;
102         match self.path {
103             Some(ref p) => {
104                 fs::rmdir_recursive(p)
105             }
106             None => Ok(())
107         }
108     }
109 }
110
111 impl Drop for TempDir {
112     fn drop(&mut self) {
113         if !self.disarmed {
114             let _ = self.cleanup_dir();
115         }
116     }
117 }
118
119 // the tests for this module need to change the path using change_dir,
120 // and this doesn't play nicely with other tests so these unit tests are located
121 // in src/test/run-pass/tempfile.rs