]> git.proxmox.com Git - rustc.git/blob - src/vendor/same-file/src/lib.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / vendor / same-file / src / lib.rs
1 /*!
2 This crate provides a safe and simple **cross platform** way to determine
3 whether two file paths refer to the same file or directory.
4
5 Most uses of this crate should be limited to the top-level `is_same_file`
6 function, which takes two file paths and returns true if they refer to the
7 same file or directory:
8
9 ```rust,no_run
10 # fn example() -> ::std::io::Result<()> {
11 use same_file::is_same_file;
12
13 assert!(try!(is_same_file("/bin/sh", "/usr/bin/sh")));
14 # Ok(()) } example().unwrap();
15 ```
16
17 Additionally, this crate provides a `Handle` type that permits a more efficient
18 equality check depending on your access pattern. For example, if one wanted to
19 checked whether any path in a list of paths corresponded to the process' stdout
20 handle, then one could build a handle once for stdout. The equality check for
21 each file in the list then only requires one stat call instead of two. The code
22 might look like this:
23
24 ```rust,no_run
25 # fn example() -> ::std::io::Result<()> {
26 use same_file::Handle;
27
28 let candidates = &[
29 "examples/is_same_file.rs",
30 "examples/is_stderr.rs",
31 "examples/stderr",
32 ];
33 let stdout_handle = try!(Handle::stdout());
34 for candidate in candidates {
35 let handle = try!(Handle::from_path(candidate));
36 if stdout_handle == handle {
37 println!("{:?} is stdout!", candidate);
38 } else {
39 println!("{:?} is NOT stdout!", candidate);
40 }
41 }
42 # Ok(()) } example().unwrap();
43 ```
44
45 See `examples/is_stderr.rs` for a runnable example. Compare the output of
46 `cargo run is_stderr 2> examples/stderr` and `cargo run is_stderr`.
47 */
48
49 #![deny(missing_docs)]
50
51 #[cfg(windows)]
52 extern crate kernel32;
53 #[cfg(windows)]
54 extern crate winapi;
55
56 use std::fs::File;
57 use std::io;
58 use std::path::Path;
59
60 #[cfg(any(target_os = "redox", unix))]
61 use unix as imp;
62 #[cfg(windows)]
63 use win as imp;
64
65 #[cfg(any(target_os = "redox", unix))]
66 mod unix;
67 #[cfg(windows)]
68 mod win;
69
70 /// A handle to a file that can be tested for equality with other handles.
71 ///
72 /// If two files are the same, then any two handles of those files will compare
73 /// equal. If two files are not the same, then any two handles of those files
74 /// will compare not-equal.
75 ///
76 /// A handle consumes an open file resource as long as it exists.
77 ///
78 /// Note that it's possible for comparing two handles to produce a false
79 /// positive on some platforms. Namely, two handles can compare equal even if
80 /// the two handles *don't* point to the same file.
81 #[derive(Debug, Eq, PartialEq)]
82 pub struct Handle(imp::Handle);
83
84 impl Handle {
85 /// Construct a handle from a path.
86 ///
87 /// Note that the underlying `File` is opened in read-only mode on all
88 /// platforms.
89 pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
90 imp::Handle::from_path(p).map(Handle)
91 }
92
93 /// Construct a handle from a file.
94 pub fn from_file(file: File) -> io::Result<Handle> {
95 imp::Handle::from_file(file).map(Handle)
96 }
97
98 /// Construct a handle from stdin.
99 pub fn stdin() -> io::Result<Handle> {
100 imp::Handle::stdin().map(Handle)
101 }
102
103 /// Construct a handle from stdout.
104 pub fn stdout() -> io::Result<Handle> {
105 imp::Handle::stdout().map(Handle)
106 }
107
108 /// Construct a handle from stderr.
109 pub fn stderr() -> io::Result<Handle> {
110 imp::Handle::stderr().map(Handle)
111 }
112
113 /// Return a reference to the underlying file.
114 pub fn as_file(&self) -> &File {
115 self.0.as_file()
116 }
117
118 /// Return a mutable reference to the underlying file.
119 pub fn as_file_mut(&mut self) -> &mut File {
120 self.0.as_file_mut()
121 }
122
123 /// Return the underlying device number of this handle.
124 #[cfg(any(target_os = "redox", unix))]
125 pub fn dev(&self) -> u64 {
126 self.0.dev()
127 }
128
129 /// Return the underlying inode number of this handle.
130 #[cfg(any(target_os = "redox", unix))]
131 pub fn ino(&self) -> u64 {
132 self.0.ino()
133 }
134 }
135
136 /// Returns true if the two file paths may correspond to the same file.
137 ///
138 /// If there was a problem accessing either file path, then an error is
139 /// returned.
140 ///
141 /// Note that it's possible for this to produce a false positive on some
142 /// platforms. Namely, this can return true even if the two file paths *don't*
143 /// resolve to the same file.
144 ///
145 /// # Example
146 ///
147 /// ```rust,no_run
148 /// use same_file::is_same_file;
149 ///
150 /// assert!(is_same_file("./foo", "././foo").unwrap_or(false));
151 /// ```
152 pub fn is_same_file<P, Q>(
153 path1: P,
154 path2: Q,
155 ) -> io::Result<bool> where P: AsRef<Path>, Q: AsRef<Path> {
156 Ok(try!(Handle::from_path(path1)) == try!(Handle::from_path(path2)))
157 }
158
159 #[cfg(test)]
160 mod tests {
161 extern crate rand;
162
163 use std::env;
164 use std::fs::{self, File};
165 use std::io;
166 use std::path::{Path, PathBuf};
167
168 use self::rand::Rng;
169
170 use super::is_same_file;
171
172 struct TempDir(PathBuf);
173
174 impl TempDir {
175 fn path<'a>(&'a self) -> &'a Path {
176 &self.0
177 }
178 }
179
180 impl Drop for TempDir {
181 fn drop(&mut self) {
182 fs::remove_dir_all(&self.0).unwrap();
183 }
184 }
185
186 fn tmpdir() -> TempDir {
187 let p = env::temp_dir();
188 let mut r = self::rand::thread_rng();
189 let ret = p.join(&format!("rust-{}", r.next_u32()));
190 fs::create_dir(&ret).unwrap();
191 TempDir(ret)
192 }
193
194 #[cfg(unix)]
195 pub fn soft_link_dir<P: AsRef<Path>, Q: AsRef<Path>>(
196 src: P,
197 dst: Q,
198 ) -> io::Result<()> {
199 use std::os::unix::fs::symlink;
200 symlink(src, dst)
201 }
202
203 #[cfg(unix)]
204 pub fn soft_link_file<P: AsRef<Path>, Q: AsRef<Path>>(
205 src: P,
206 dst: Q,
207 ) -> io::Result<()> {
208 soft_link_dir(src, dst)
209 }
210
211 #[cfg(windows)]
212 pub fn soft_link_dir<P: AsRef<Path>, Q: AsRef<Path>>(
213 src: P,
214 dst: Q,
215 ) -> io::Result<()> {
216 use std::os::windows::fs::symlink_dir;
217 symlink_dir(src, dst)
218 }
219
220 #[cfg(windows)]
221 pub fn soft_link_file<P: AsRef<Path>, Q: AsRef<Path>>(
222 src: P,
223 dst: Q,
224 ) -> io::Result<()> {
225 use std::os::windows::fs::symlink_file;
226 symlink_file(src, dst)
227 }
228
229 // These tests are rather uninteresting. The really interesting tests
230 // would stress the edge cases. On Unix, this might be comparing two files
231 // on different mount points with the same inode number. On Windows, this
232 // might be comparing two files whose file indices are the same on file
233 // systems where such things aren't guaranteed to be unique.
234 //
235 // Alas, I don't know how to create those environmental conditions. ---AG
236
237 #[test]
238 fn same_file_trivial() {
239 let tdir = tmpdir();
240 let dir = tdir.path();
241
242 File::create(dir.join("a")).unwrap();
243 assert!(is_same_file(dir.join("a"), dir.join("a")).unwrap());
244 }
245
246 #[test]
247 fn same_dir_trivial() {
248 let tdir = tmpdir();
249 let dir = tdir.path();
250
251 fs::create_dir(dir.join("a")).unwrap();
252 assert!(is_same_file(dir.join("a"), dir.join("a")).unwrap());
253 }
254
255 #[test]
256 fn not_same_file_trivial() {
257 let tdir = tmpdir();
258 let dir = tdir.path();
259
260 File::create(dir.join("a")).unwrap();
261 File::create(dir.join("b")).unwrap();
262 assert!(!is_same_file(dir.join("a"), dir.join("b")).unwrap());
263 }
264
265 #[test]
266 fn not_same_dir_trivial() {
267 let tdir = tmpdir();
268 let dir = tdir.path();
269
270 fs::create_dir(dir.join("a")).unwrap();
271 fs::create_dir(dir.join("b")).unwrap();
272 assert!(!is_same_file(dir.join("a"), dir.join("b")).unwrap());
273 }
274
275 #[test]
276 fn same_file_hard() {
277 let tdir = tmpdir();
278 let dir = tdir.path();
279
280 File::create(dir.join("a")).unwrap();
281 fs::hard_link(dir.join("a"), dir.join("alink")).unwrap();
282 assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
283 }
284
285 #[test]
286 fn same_file_soft() {
287 let tdir = tmpdir();
288 let dir = tdir.path();
289
290 File::create(dir.join("a")).unwrap();
291 soft_link_file(dir.join("a"), dir.join("alink")).unwrap();
292 assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
293 }
294
295 #[test]
296 fn same_dir_soft() {
297 let tdir = tmpdir();
298 let dir = tdir.path();
299
300 fs::create_dir(dir.join("a")).unwrap();
301 soft_link_dir(dir.join("a"), dir.join("alink")).unwrap();
302 assert!(is_same_file(dir.join("a"), dir.join("alink")).unwrap());
303 }
304 }