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