]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/tempfile.rs
a232231733d8bacd58efddb620f8b89a78c3c647
[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 unwrap(self) -> Path {
77         let mut tmpdir = self;
78         tmpdir.path.take().unwrap()
79     }
80
81     /// Access the wrapped `std::path::Path` to the temporary directory.
82     pub fn path<'a>(&'a self) -> &'a Path {
83         self.path.as_ref().unwrap()
84     }
85
86     /// Close and remove the temporary directory
87     ///
88     /// Although `TempDir` removes the directory on drop, in the destructor
89     /// any errors are ignored. To detect errors cleaning up the temporary
90     /// directory, call `close` instead.
91     pub fn close(mut self) -> IoResult<()> {
92         self.cleanup_dir()
93     }
94
95     fn cleanup_dir(&mut self) -> IoResult<()> {
96         assert!(!self.disarmed);
97         self.disarmed = true;
98         match self.path {
99             Some(ref p) => {
100                 fs::rmdir_recursive(p)
101             }
102             None => Ok(())
103         }
104     }
105 }
106
107 impl Drop for TempDir {
108     fn drop(&mut self) {
109         if !self.disarmed {
110             let _ = self.cleanup_dir();
111         }
112     }
113 }
114
115 // the tests for this module need to change the path using change_dir,
116 // and this doesn't play nicely with other tests so these unit tests are located
117 // in src/test/run-pass/tempfile.rs