├── .gitignore ├── appveyor.yml ├── .travis.yml ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── tests └── smoke.rs ├── LICENSE-APACHE └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - TARGET: x86_64-pc-windows-msvc 4 | - TARGET: i686-pc-windows-msvc 5 | - TARGET: i686-pc-windows-gnu 6 | install: 7 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" 8 | - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" 9 | - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin 10 | - SET PATH=%PATH%;C:\MinGW\bin 11 | - rustc -V 12 | - cargo -V 13 | 14 | build: false 15 | 16 | test_script: 17 | - cargo test --verbose 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: 3 | on_success: never 4 | 5 | language: rust 6 | sudo: false 7 | 8 | matrix: 9 | include: 10 | - rust: stable 11 | - os: osx 12 | - rust: beta 13 | - rust: nightly 14 | 15 | - rust: nightly 16 | before_script: 17 | - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH 18 | script: 19 | - cargo doc --no-deps --all-features 20 | after_success: 21 | - travis-cargo --only nightly doc-upload 22 | 23 | env: 24 | global: 25 | secure: "iENCKNKfyveEXk+OofJDnxGfeVnxHovvLsrtl7eUBev6YGIIlUISZliyCvBrSkZKTeLfdDEP+Q5Mv9tVOMAx/FNiKB9V07WFDahD6xvz+qd3k790gdzgy+0LcNKskBVzDK62zCofvknGVbQa9f5iimhu+wKs9bb+WCxicVWmeRo=" 26 | 27 | notifications: 28 | email: 29 | on_success: never 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "tempdir" 4 | # NB: When modifying, also modify html_root_url in lib.rs 5 | version = "0.3.7" 6 | authors = ["The Rust Project Developers"] 7 | license = "MIT/Apache-2.0" 8 | readme = "README.md" 9 | repository = "https://github.com/rust-lang/tempdir" 10 | homepage = "https://github.com/rust-lang/tempdir" 11 | documentation = "https://doc.rust-lang.org/tempdir" 12 | description = """ 13 | A library for managing a temporary directory and deleting all contents when it's 14 | dropped. 15 | """ 16 | categories = ["filesystem"] 17 | keywords = ["fs", "file", "filesystem"] 18 | 19 | [badges] 20 | travis-ci = { repository = "rust-lang-nursery/tempdir" } 21 | appveyor = { repository = "rust-lang-libs/tempdir" } 22 | 23 | [dependencies] 24 | rand = "0.4" 25 | remove_dir_all = "0.5" 26 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tempdir 2 | ======= 3 | 4 | A Rust library for creating a temporary directory and deleting its entire 5 | contents when the directory is dropped. 6 | 7 | [![Build Status](https://travis-ci.org/rust-lang-nursery/tempdir.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/tempdir) 8 | [![Build status](https://ci.appveyor.com/api/projects/status/2mp24396db5t4hul/branch/master?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/tempdir/branch/master) 9 | 10 | [Documentation](https://doc.rust-lang.org/tempdir) 11 | 12 | ## Deprecation Note 13 | 14 | The `tempdir` crate is being merged into [`tempfile`](https://github.com/Stebalien/tempfile) and is available in `3.x`. Please direct new issues and pull requests to `tempfile`. 15 | 16 | ## Usage 17 | 18 | Add this to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | tempdir = "0.3" 23 | ``` 24 | 25 | and this to your crate root: 26 | 27 | ```rust 28 | extern crate tempdir; 29 | ``` 30 | 31 | ## Example 32 | 33 | This sample method does the following: 34 | 35 | 1. Create a temporary directory in the default location with the given prefix. 36 | 2. Determine a file path in the directory and print it out. 37 | 3. Create a file inside the temp folder. 38 | 4. Write to the file and sync it to disk. 39 | 5. Close the directory, deleting the contents in the process. 40 | 41 | ```rust 42 | use std::io::{self, Write}; 43 | use std::fs::File; 44 | use tempdir::TempDir; 45 | 46 | fn write_temp_folder_with_files() -> io::Result<()> { 47 | let dir = TempDir::new("my_directory_prefix")?; 48 | let file_path = dir.path().join("foo.txt"); 49 | println!("{:?}", file_path); 50 | 51 | let mut f = File::create(file_path)?; 52 | f.write_all(b"Hello, world!")?; 53 | f.sync_all()?; 54 | dir.close()?; 55 | 56 | Ok(()) 57 | } 58 | ``` 59 | 60 | **Note:** Closing the directory is actually optional, as it would be done on 61 | drop. The benefit of closing here is that it allows possible errors to be 62 | handled. 63 | -------------------------------------------------------------------------------- /tests/smoke.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2014 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 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | extern crate tempdir; 12 | 13 | use std::env; 14 | use std::fs; 15 | use std::path::Path; 16 | use std::sync::mpsc::channel; 17 | use std::thread; 18 | 19 | use tempdir::TempDir; 20 | 21 | macro_rules! t { 22 | ($e:expr) => (match $e { Ok(n) => n, Err(e) => panic!("error: {}", e) }) 23 | } 24 | 25 | trait PathExt { 26 | fn exists(&self) -> bool; 27 | fn is_dir(&self) -> bool; 28 | } 29 | 30 | impl PathExt for Path { 31 | fn exists(&self) -> bool { fs::metadata(self).is_ok() } 32 | fn is_dir(&self) -> bool { 33 | fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) 34 | } 35 | } 36 | 37 | fn test_tempdir() { 38 | let path = { 39 | let p = t!(TempDir::new_in(&Path::new("."), "foobar")); 40 | let p = p.path(); 41 | assert!(p.to_str().unwrap().contains("foobar")); 42 | p.to_path_buf() 43 | }; 44 | assert!(!path.exists()); 45 | } 46 | 47 | fn test_rm_tempdir() { 48 | let (tx, rx) = channel(); 49 | let f = move|| -> () { 50 | let tmp = t!(TempDir::new("test_rm_tempdir")); 51 | tx.send(tmp.path().to_path_buf()).unwrap(); 52 | panic!("panic to unwind past `tmp`"); 53 | }; 54 | let _ = thread::spawn(f).join(); 55 | let path = rx.recv().unwrap(); 56 | assert!(!path.exists()); 57 | 58 | let tmp = t!(TempDir::new("test_rm_tempdir")); 59 | let path = tmp.path().to_path_buf(); 60 | let f = move|| -> () { 61 | let _tmp = tmp; 62 | panic!("panic to unwind past `tmp`"); 63 | }; 64 | let _ = thread::spawn(f).join(); 65 | assert!(!path.exists()); 66 | 67 | let path; 68 | { 69 | let f = move || { 70 | t!(TempDir::new("test_rm_tempdir")) 71 | }; 72 | 73 | let tmp = thread::spawn(f).join().unwrap(); 74 | path = tmp.path().to_path_buf(); 75 | assert!(path.exists()); 76 | } 77 | assert!(!path.exists()); 78 | 79 | let path; 80 | { 81 | let tmp = t!(TempDir::new("test_rm_tempdir")); 82 | path = tmp.into_path(); 83 | } 84 | assert!(path.exists()); 85 | t!(fs::remove_dir_all(&path)); 86 | assert!(!path.exists()); 87 | } 88 | 89 | fn test_rm_tempdir_close() { 90 | let (tx, rx) = channel(); 91 | let f = move|| -> () { 92 | let tmp = t!(TempDir::new("test_rm_tempdir")); 93 | tx.send(tmp.path().to_path_buf()).unwrap(); 94 | t!(tmp.close()); 95 | panic!("panic when unwinding past `tmp`"); 96 | }; 97 | let _ = thread::spawn(f).join(); 98 | let path = rx.recv().unwrap(); 99 | assert!(!path.exists()); 100 | 101 | let tmp = t!(TempDir::new("test_rm_tempdir")); 102 | let path = tmp.path().to_path_buf(); 103 | let f = move|| -> () { 104 | let tmp = tmp; 105 | t!(tmp.close()); 106 | panic!("panic when unwinding past `tmp`"); 107 | }; 108 | let _ = thread::spawn(f).join(); 109 | assert!(!path.exists()); 110 | 111 | let path; 112 | { 113 | let f = move || { 114 | t!(TempDir::new("test_rm_tempdir")) 115 | }; 116 | 117 | let tmp = thread::spawn(f).join().unwrap(); 118 | path = tmp.path().to_path_buf(); 119 | assert!(path.exists()); 120 | t!(tmp.close()); 121 | } 122 | assert!(!path.exists()); 123 | 124 | let path; 125 | { 126 | let tmp = t!(TempDir::new("test_rm_tempdir")); 127 | path = tmp.into_path(); 128 | } 129 | assert!(path.exists()); 130 | t!(fs::remove_dir_all(&path)); 131 | assert!(!path.exists()); 132 | } 133 | 134 | // Ideally these would be in std::os but then core would need 135 | // to depend on std 136 | fn recursive_mkdir_rel() { 137 | let path = Path::new("frob"); 138 | let cwd = env::current_dir().unwrap(); 139 | println!("recursive_mkdir_rel: Making: {} in cwd {} [{}]", path.display(), 140 | cwd.display(), path.exists()); 141 | t!(fs::create_dir(&path)); 142 | assert!(path.is_dir()); 143 | t!(fs::create_dir_all(&path)); 144 | assert!(path.is_dir()); 145 | } 146 | 147 | fn recursive_mkdir_dot() { 148 | let dot = Path::new("."); 149 | t!(fs::create_dir_all(&dot)); 150 | let dotdot = Path::new(".."); 151 | t!(fs::create_dir_all(&dotdot)); 152 | } 153 | 154 | fn recursive_mkdir_rel_2() { 155 | let path = Path::new("./frob/baz"); 156 | let cwd = env::current_dir().unwrap(); 157 | println!("recursive_mkdir_rel_2: Making: {} in cwd {} [{}]", path.display(), 158 | cwd.display(), path.exists()); 159 | t!(fs::create_dir_all(&path)); 160 | assert!(path.is_dir()); 161 | assert!(path.parent().unwrap().is_dir()); 162 | let path2 = Path::new("quux/blat"); 163 | println!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(), 164 | cwd.display()); 165 | t!(fs::create_dir("quux")); 166 | t!(fs::create_dir_all(&path2)); 167 | assert!(path2.is_dir()); 168 | assert!(path2.parent().unwrap().is_dir()); 169 | } 170 | 171 | // Ideally this would be in core, but needs TempFile 172 | pub fn test_remove_dir_all_ok() { 173 | let tmpdir = t!(TempDir::new("test")); 174 | let tmpdir = tmpdir.path(); 175 | let root = tmpdir.join("foo"); 176 | 177 | println!("making {}", root.display()); 178 | t!(fs::create_dir(&root)); 179 | t!(fs::create_dir(&root.join("foo"))); 180 | t!(fs::create_dir(&root.join("foo").join("bar"))); 181 | t!(fs::create_dir(&root.join("foo").join("bar").join("blat"))); 182 | t!(fs::remove_dir_all(&root)); 183 | assert!(!root.exists()); 184 | assert!(!root.join("bar").exists()); 185 | assert!(!root.join("bar").join("blat").exists()); 186 | } 187 | 188 | pub fn dont_double_panic() { 189 | let r: Result<(), _> = thread::spawn(move|| { 190 | let tmpdir = TempDir::new("test").unwrap(); 191 | // Remove the temporary directory so that TempDir sees 192 | // an error on drop 193 | t!(fs::remove_dir(tmpdir.path())); 194 | // Panic. If TempDir panics *again* due to the rmdir 195 | // error then the process will abort. 196 | panic!(); 197 | }).join(); 198 | assert!(r.is_err()); 199 | } 200 | 201 | fn in_tmpdir(f: F) where F: FnOnce() { 202 | let tmpdir = t!(TempDir::new("test")); 203 | assert!(env::set_current_dir(tmpdir.path()).is_ok()); 204 | 205 | f(); 206 | } 207 | 208 | pub fn pass_as_asref_path() { 209 | let tempdir = t!(TempDir::new("test")); 210 | takes_asref_path(&tempdir); 211 | 212 | fn takes_asref_path>(path: T) { 213 | let path = path.as_ref(); 214 | assert!(path.exists()); 215 | } 216 | } 217 | 218 | #[test] 219 | fn main() { 220 | in_tmpdir(test_tempdir); 221 | in_tmpdir(test_rm_tempdir); 222 | in_tmpdir(test_rm_tempdir_close); 223 | in_tmpdir(recursive_mkdir_rel); 224 | in_tmpdir(recursive_mkdir_dot); 225 | in_tmpdir(recursive_mkdir_rel_2); 226 | in_tmpdir(test_remove_dir_all_ok); 227 | in_tmpdir(dont_double_panic); 228 | in_tmpdir(pass_as_asref_path); 229 | } 230 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 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 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", 12 | html_favicon_url = "https://www.rust-lang.org/favicon.ico", 13 | html_root_url = "https://docs.rs/tempdir/0.3.7")] 14 | #![cfg_attr(test, deny(warnings))] 15 | 16 | //! Temporary directories of files. 17 | //! 18 | //! The [`TempDir`] type creates a directory on the file system that 19 | //! is deleted once it goes out of scope. At construction, the 20 | //! `TempDir` creates a new directory with a randomly generated name 21 | //! and a prefix of your choosing. 22 | //! 23 | //! [`TempDir`]: struct.TempDir.html 24 | //! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html 25 | //! 26 | //! # Examples 27 | //! 28 | //! ``` 29 | //! extern crate tempdir; 30 | //! 31 | //! use std::fs::File; 32 | //! use std::io::{self, Write}; 33 | //! use tempdir::TempDir; 34 | //! 35 | //! fn main() { 36 | //! if let Err(_) = run() { 37 | //! ::std::process::exit(1); 38 | //! } 39 | //! } 40 | //! 41 | //! fn run() -> Result<(), io::Error> { 42 | //! // Create a directory inside of `std::env::temp_dir()`, named with 43 | //! // the prefix "example". 44 | //! let tmp_dir = TempDir::new("example")?; 45 | //! let file_path = tmp_dir.path().join("my-temporary-note.txt"); 46 | //! let mut tmp_file = File::create(file_path)?; 47 | //! writeln!(tmp_file, "Brian was here. Briefly.")?; 48 | //! 49 | //! // By closing the `TempDir` explicitly, we can check that it has 50 | //! // been deleted successfully. If we don't close it explicitly, 51 | //! // the directory will still be deleted when `tmp_dir` goes out 52 | //! // of scope, but we won't know whether deleting the directory 53 | //! // succeeded. 54 | //! drop(tmp_file); 55 | //! tmp_dir.close()?; 56 | //! Ok(()) 57 | //! } 58 | //! ``` 59 | 60 | extern crate rand; 61 | extern crate remove_dir_all; 62 | 63 | use std::env; 64 | use std::io::{self, Error, ErrorKind}; 65 | use std::fmt; 66 | use std::fs; 67 | use std::path::{self, PathBuf, Path}; 68 | use rand::{thread_rng, Rng}; 69 | use remove_dir_all::remove_dir_all; 70 | 71 | /// A directory in the filesystem that is automatically deleted when 72 | /// it goes out of scope. 73 | /// 74 | /// The [`TempDir`] type creates a directory on the file system that 75 | /// is deleted once it goes out of scope. At construction, the 76 | /// `TempDir` creates a new directory with a randomly generated name, 77 | /// and with a prefix of your choosing. 78 | /// 79 | /// The default constructor, [`TempDir::new`], creates directories in 80 | /// the location returned by [`std::env::temp_dir()`], but `TempDir` 81 | /// can be configured to manage a temporary directory in any location 82 | /// by constructing with [`TempDir::new_in`]. 83 | /// 84 | /// After creating a `TempDir`, work with the file system by doing 85 | /// standard [`std::fs`] file system operations on its [`Path`], 86 | /// which can be retrieved with [`TempDir::path`]. Once the `TempDir` 87 | /// value is dropped, the directory at the path will be deleted, along 88 | /// with any files and directories it contains. It is your responsibility 89 | /// to ensure that no further file system operations are attempted 90 | /// inside the temporary directory once it has been deleted. 91 | /// 92 | /// Various platform-specific conditions may cause `TempDir` to fail 93 | /// to delete the underlying directory. It's important to ensure that 94 | /// handles (like [`File`] and [`ReadDir`]) to files inside the 95 | /// directory are dropped before the `TempDir` goes out of scope. The 96 | /// `TempDir` destructor will silently ignore any errors in deleting 97 | /// the directory; to instead handle errors call [`TempDir::close`]. 98 | /// 99 | /// Note that if the program exits before the `TempDir` destructor is 100 | /// run, such as via [`std::process::exit`], by segfaulting, or by 101 | /// receiving a signal like `SIGINT`, then the temporary directory 102 | /// will not be deleted. 103 | /// 104 | /// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html 105 | /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html 106 | /// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html 107 | /// [`TempDir::close`]: struct.TempDir.html#method.close 108 | /// [`TempDir::new`]: struct.TempDir.html#method.new 109 | /// [`TempDir::new_in`]: struct.TempDir.html#method.new_in 110 | /// [`TempDir::path`]: struct.TempDir.html#method.path 111 | /// [`TempDir`]: struct.TempDir.html 112 | /// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html 113 | /// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html 114 | /// [`std::process::exit`]: http://doc.rust-lang.org/std/process/fn.exit.html 115 | pub struct TempDir { 116 | path: Option, 117 | } 118 | 119 | // How many times should we (re)try finding an unused random name? It should be 120 | // enough that an attacker will run out of luck before we run out of patience. 121 | const NUM_RETRIES: u32 = 1 << 31; 122 | // How many characters should we include in a random file name? It needs to 123 | // be enough to dissuade an attacker from trying to preemptively create names 124 | // of that length, but not so huge that we unnecessarily drain the random number 125 | // generator of entropy. 126 | const NUM_RAND_CHARS: usize = 12; 127 | 128 | impl TempDir { 129 | /// Attempts to make a temporary directory inside of `env::temp_dir()` whose 130 | /// name will have the prefix, `prefix`. The directory and 131 | /// everything inside it will be automatically deleted once the 132 | /// returned `TempDir` is destroyed. 133 | /// 134 | /// # Errors 135 | /// 136 | /// If the directory can not be created, `Err` is returned. 137 | /// 138 | /// # Examples 139 | /// 140 | /// ``` 141 | /// use std::fs::File; 142 | /// use std::io::Write; 143 | /// use tempdir::TempDir; 144 | /// 145 | /// # use std::io; 146 | /// # fn run() -> Result<(), io::Error> { 147 | /// // Create a directory inside of `std::env::temp_dir()`, named with 148 | /// // the prefix, "example". 149 | /// let tmp_dir = TempDir::new("example")?; 150 | /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); 151 | /// let mut tmp_file = File::create(file_path)?; 152 | /// writeln!(tmp_file, "Brian was here. Briefly.")?; 153 | /// 154 | /// // `tmp_dir` goes out of scope, the directory as well as 155 | /// // `tmp_file` will be deleted here. 156 | /// # Ok(()) 157 | /// # } 158 | /// ``` 159 | pub fn new(prefix: &str) -> io::Result { 160 | TempDir::new_in(&env::temp_dir(), prefix) 161 | } 162 | 163 | /// Attempts to make a temporary directory inside of `tmpdir` 164 | /// whose name will have the prefix `prefix`. The directory and 165 | /// everything inside it will be automatically deleted once the 166 | /// returned `TempDir` is destroyed. 167 | /// 168 | /// # Errors 169 | /// 170 | /// If the directory can not be created, `Err` is returned. 171 | /// 172 | /// # Examples 173 | /// 174 | /// ``` 175 | /// use std::fs::{self, File}; 176 | /// use std::io::Write; 177 | /// use tempdir::TempDir; 178 | /// 179 | /// # use std::io; 180 | /// # fn run() -> Result<(), io::Error> { 181 | /// // Create a directory inside of the current directory, named with 182 | /// // the prefix, "example". 183 | /// let tmp_dir = TempDir::new_in(".", "example")?; 184 | /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); 185 | /// let mut tmp_file = File::create(file_path)?; 186 | /// writeln!(tmp_file, "Brian was here. Briefly.")?; 187 | /// # Ok(()) 188 | /// # } 189 | /// ``` 190 | pub fn new_in>(tmpdir: P, prefix: &str) -> io::Result { 191 | let storage; 192 | let mut tmpdir = tmpdir.as_ref(); 193 | if !tmpdir.is_absolute() { 194 | let cur_dir = env::current_dir()?; 195 | storage = cur_dir.join(tmpdir); 196 | tmpdir = &storage; 197 | // return TempDir::new_in(&cur_dir.join(tmpdir), prefix); 198 | } 199 | 200 | let mut rng = thread_rng(); 201 | for _ in 0..NUM_RETRIES { 202 | let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect(); 203 | let leaf = if !prefix.is_empty() { 204 | format!("{}.{}", prefix, suffix) 205 | } else { 206 | // If we're given an empty string for a prefix, then creating a 207 | // directory starting with "." would lead to it being 208 | // semi-invisible on some systems. 209 | suffix 210 | }; 211 | let path = tmpdir.join(&leaf); 212 | match fs::create_dir(&path) { 213 | Ok(_) => return Ok(TempDir { path: Some(path) }), 214 | Err(ref e) if e.kind() == ErrorKind::AlreadyExists => {} 215 | Err(e) => return Err(e), 216 | } 217 | } 218 | 219 | Err(Error::new(ErrorKind::AlreadyExists, 220 | "too many temporary directories already exist")) 221 | } 222 | 223 | /// Accesses the [`Path`] to the temporary directory. 224 | /// 225 | /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html 226 | /// 227 | /// # Examples 228 | /// 229 | /// ``` 230 | /// use tempdir::TempDir; 231 | /// 232 | /// # use std::io; 233 | /// # fn run() -> Result<(), io::Error> { 234 | /// let tmp_path; 235 | /// 236 | /// { 237 | /// let tmp_dir = TempDir::new("example")?; 238 | /// tmp_path = tmp_dir.path().to_owned(); 239 | /// 240 | /// // Check that the temp directory actually exists. 241 | /// assert!(tmp_path.exists()); 242 | /// 243 | /// // End of `tmp_dir` scope, directory will be deleted 244 | /// } 245 | /// 246 | /// // Temp directory should be deleted by now 247 | /// assert_eq!(tmp_path.exists(), false); 248 | /// # Ok(()) 249 | /// # } 250 | /// ``` 251 | pub fn path(&self) -> &path::Path { 252 | self.path.as_ref().unwrap() 253 | } 254 | 255 | /// Unwraps the [`Path`] contained in the `TempDir` and 256 | /// returns it. This destroys the `TempDir` without deleting the 257 | /// directory represented by the returned `Path`. 258 | /// 259 | /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html 260 | /// 261 | /// # Examples 262 | /// 263 | /// ``` 264 | /// use std::fs; 265 | /// use tempdir::TempDir; 266 | /// 267 | /// # use std::io; 268 | /// # fn run() -> Result<(), io::Error> { 269 | /// let tmp_dir = TempDir::new("example")?; 270 | /// 271 | /// // Convert `tmp_dir` into a `Path`, destroying the `TempDir` 272 | /// // without deleting the directory. 273 | /// let tmp_path = tmp_dir.into_path(); 274 | /// 275 | /// // Delete the temporary directory ourselves. 276 | /// fs::remove_dir_all(tmp_path)?; 277 | /// # Ok(()) 278 | /// # } 279 | /// ``` 280 | pub fn into_path(mut self) -> PathBuf { 281 | self.path.take().unwrap() 282 | } 283 | 284 | /// Closes and removes the temporary directory, returing a `Result`. 285 | /// 286 | /// Although `TempDir` removes the directory on drop, in the destructor 287 | /// any errors are ignored. To detect errors cleaning up the temporary 288 | /// directory, call `close` instead. 289 | /// 290 | /// # Errors 291 | /// 292 | /// This function may return a variety of [`std::io::Error`]s that result from deleting 293 | /// the files and directories contained with the temporary directory, 294 | /// as well as from deleting the temporary directory itself. These errors 295 | /// may be platform specific. 296 | /// 297 | /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html 298 | /// 299 | /// # Examples 300 | /// 301 | /// ``` 302 | /// use std::fs::File; 303 | /// use std::io::Write; 304 | /// use tempdir::TempDir; 305 | /// 306 | /// # use std::io; 307 | /// # fn run() -> Result<(), io::Error> { 308 | /// // Create a directory inside of `std::env::temp_dir()`, named with 309 | /// // the prefix, "example". 310 | /// let tmp_dir = TempDir::new("example")?; 311 | /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); 312 | /// let mut tmp_file = File::create(file_path)?; 313 | /// writeln!(tmp_file, "Brian was here. Briefly.")?; 314 | /// 315 | /// // By closing the `TempDir` explicitly we can check that it has 316 | /// // been deleted successfully. If we don't close it explicitly, 317 | /// // the directory will still be deleted when `tmp_dir` goes out 318 | /// // of scope, but we won't know whether deleting the directory 319 | /// // succeeded. 320 | /// drop(tmp_file); 321 | /// tmp_dir.close()?; 322 | /// # Ok(()) 323 | /// # } 324 | /// ``` 325 | pub fn close(mut self) -> io::Result<()> { 326 | let result = remove_dir_all(self.path()); 327 | 328 | // Prevent the Drop impl from removing the dir a second time. 329 | self.path = None; 330 | 331 | result 332 | } 333 | } 334 | 335 | impl AsRef for TempDir { 336 | fn as_ref(&self) -> &Path { 337 | self.path() 338 | } 339 | } 340 | 341 | impl fmt::Debug for TempDir { 342 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 343 | f.debug_struct("TempDir") 344 | .field("path", &self.path()) 345 | .finish() 346 | } 347 | } 348 | 349 | impl Drop for TempDir { 350 | fn drop(&mut self) { 351 | // Path is `None` if `close()` or `into_path()` has been called. 352 | if let Some(ref p) = self.path { 353 | let _ = remove_dir_all(p); 354 | } 355 | } 356 | } 357 | 358 | // the tests for this module need to change the path using change_dir, 359 | // and this doesn't play nicely with other tests so these unit tests are located 360 | // in src/test/run-pass/tempfile.rs 361 | --------------------------------------------------------------------------------