]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/tempfile.rs
Merge pull request #20510 from tshepang/patch-6
[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;
18 use option::Option::{None, Some};
19 use os;
20 use path::{Path, GenericPath};
21 use result::Result::{Ok, Err};
22 use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
23
24 /// A wrapper for a path to temporary directory implementing automatic
25 /// scope-based deletion.
26 ///
27 /// # Examples
28 ///
29 /// ```no_run
30 /// use std::io::TempDir;
31 ///
32 /// {
33 ///     // create a temporary directory
34 ///     let tmpdir = match TempDir::new("mysuffix") {
35 ///         Ok(dir) => dir,
36 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
37 ///     };
38 ///
39 ///     // get the path of the temporary directory without affecting the wrapper
40 ///     let tmppath = tmpdir.path();
41 ///
42 ///     println!("The path of temporary directory is {}", tmppath.display());
43 ///
44 ///     // the temporary directory is automatically removed when tmpdir goes
45 ///     // out of scope at the end of the block
46 /// }
47 /// {
48 ///     // create a temporary directory, this time using a custom path
49 ///     let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "mysuffix") {
50 ///         Ok(dir) => dir,
51 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
52 ///     };
53 ///
54 ///     // get the path of the temporary directory and disable automatic deletion in the wrapper
55 ///     let tmppath = tmpdir.into_inner();
56 ///
57 ///     println!("The path of the not-so-temporary directory is {}", tmppath.display());
58 ///
59 ///     // the temporary directory is not removed here
60 ///     // because the directory is detached from the wrapper
61 /// }
62 /// {
63 ///     // create a temporary directory
64 ///     let tmpdir = match TempDir::new("mysuffix") {
65 ///         Ok(dir) => dir,
66 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
67 ///     };
68 ///
69 ///     // close the temporary directory manually and check the result
70 ///     match tmpdir.close() {
71 ///         Ok(_) => println!("success!"),
72 ///         Err(e) => panic!("couldn't remove temporary directory: {}", e)
73 ///     };
74 /// }
75 /// ```
76 pub struct TempDir {
77     path: Option<Path>,
78     disarmed: bool
79 }
80
81 impl TempDir {
82     /// Attempts to make a temporary directory inside of `tmpdir` whose name
83     /// will have the suffix `suffix`. The directory will be automatically
84     /// deleted once the returned wrapper is destroyed.
85     ///
86     /// If no directory can be created, `Err` is returned.
87     pub fn new_in(tmpdir: &Path, suffix: &str) -> IoResult<TempDir> {
88         if !tmpdir.is_absolute() {
89             let abs_tmpdir = try!(os::make_absolute(tmpdir));
90             return TempDir::new_in(&abs_tmpdir, suffix);
91         }
92
93         static CNT: AtomicUint = ATOMIC_UINT_INIT;
94
95         let mut attempts = 0u;
96         loop {
97             let filename =
98                 format!("rs-{}-{}-{}",
99                         unsafe { libc::getpid() },
100                         CNT.fetch_add(1, Ordering::SeqCst),
101                         suffix);
102             let p = tmpdir.join(filename);
103             match fs::mkdir(&p, io::USER_RWX) {
104                 Err(error) => {
105                     if attempts >= 1000 {
106                         return Err(error)
107                     }
108                     attempts += 1;
109                 }
110                 Ok(()) => return Ok(TempDir { path: Some(p), disarmed: false })
111             }
112         }
113     }
114
115     /// Attempts to make a temporary directory inside of `os::tmpdir()` whose
116     /// name will have the suffix `suffix`. The directory will be automatically
117     /// deleted once the returned wrapper is destroyed.
118     ///
119     /// If no directory can be created, `Err` is returned.
120     pub fn new(suffix: &str) -> IoResult<TempDir> {
121         TempDir::new_in(&os::tmpdir(), suffix)
122     }
123
124     /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
125     /// This discards the wrapper so that the automatic deletion of the
126     /// temporary directory is prevented.
127     pub fn into_inner(self) -> Path {
128         let mut tmpdir = self;
129         tmpdir.path.take().unwrap()
130     }
131
132     /// Access the wrapped `std::path::Path` to the temporary directory.
133     pub fn path<'a>(&'a self) -> &'a Path {
134         self.path.as_ref().unwrap()
135     }
136
137     /// Close and remove the temporary directory
138     ///
139     /// Although `TempDir` removes the directory on drop, in the destructor
140     /// any errors are ignored. To detect errors cleaning up the temporary
141     /// directory, call `close` instead.
142     pub fn close(mut self) -> IoResult<()> {
143         self.cleanup_dir()
144     }
145
146     fn cleanup_dir(&mut self) -> IoResult<()> {
147         assert!(!self.disarmed);
148         self.disarmed = true;
149         match self.path {
150             Some(ref p) => {
151                 fs::rmdir_recursive(p)
152             }
153             None => Ok(())
154         }
155     }
156 }
157
158 impl Drop for TempDir {
159     fn drop(&mut self) {
160         if !self.disarmed {
161             let _ = self.cleanup_dir();
162         }
163     }
164 }
165
166 // the tests for this module need to change the path using change_dir,
167 // and this doesn't play nicely with other tests so these unit tests are located
168 // in src/test/run-pass/tempfile.rs