| 1 | use std::fs::{File, OpenOptions}; |
| 2 | use std::hash::{Hash, Hasher}; |
| 3 | use std::io; |
| 4 | use std::os::unix::fs::MetadataExt; |
| 5 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; |
| 6 | use std::path::Path; |
| 7 | |
| 8 | #[derive (Debug)] |
| 9 | pub struct Handle { |
| 10 | file: Option<File>, |
| 11 | // If is_std is true, then we don't drop the corresponding File since it |
| 12 | // will close the handle. |
| 13 | is_std: bool, |
| 14 | dev: u64, |
| 15 | ino: u64, |
| 16 | } |
| 17 | |
| 18 | impl Drop for Handle { |
| 19 | fn drop(&mut self) { |
| 20 | if self.is_std { |
| 21 | // unwrap() will not panic. Since we were able to open an |
| 22 | // std stream successfully, then `file` is guaranteed to be Some() |
| 23 | self.file.take().unwrap().into_raw_fd(); |
| 24 | } |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | impl Eq for Handle {} |
| 29 | |
| 30 | impl PartialEq for Handle { |
| 31 | fn eq(&self, other: &Handle) -> bool { |
| 32 | (self.dev, self.ino) == (other.dev, other.ino) |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | impl AsRawFd for crate::Handle { |
| 37 | fn as_raw_fd(&self) -> RawFd { |
| 38 | // unwrap() will not panic. Since we were able to open the |
| 39 | // file successfully, then `file` is guaranteed to be Some() |
| 40 | self.0.file.as_ref().take().unwrap().as_raw_fd() |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | impl IntoRawFd for crate::Handle { |
| 45 | fn into_raw_fd(mut self) -> RawFd { |
| 46 | // unwrap() will not panic. Since we were able to open the |
| 47 | // file successfully, then `file` is guaranteed to be Some() |
| 48 | self.0.file.take().unwrap().into_raw_fd() |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | impl Hash for Handle { |
| 53 | fn hash<H: Hasher>(&self, state: &mut H) { |
| 54 | self.dev.hash(state); |
| 55 | self.ino.hash(state); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl Handle { |
| 60 | pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> { |
| 61 | Handle::from_file(OpenOptions::new().read(true).open(p)?) |
| 62 | } |
| 63 | |
| 64 | pub fn from_file(file: File) -> io::Result<Handle> { |
| 65 | let md = file.metadata()?; |
| 66 | Ok(Handle { |
| 67 | file: Some(file), |
| 68 | is_std: false, |
| 69 | dev: md.dev(), |
| 70 | ino: md.ino(), |
| 71 | }) |
| 72 | } |
| 73 | |
| 74 | pub fn from_std(file: File) -> io::Result<Handle> { |
| 75 | Handle::from_file(file).map(|mut h| { |
| 76 | h.is_std = true; |
| 77 | h |
| 78 | }) |
| 79 | } |
| 80 | |
| 81 | pub fn stdin() -> io::Result<Handle> { |
| 82 | Handle::from_std(unsafe { File::from_raw_fd(0) }) |
| 83 | } |
| 84 | |
| 85 | pub fn stdout() -> io::Result<Handle> { |
| 86 | Handle::from_std(unsafe { File::from_raw_fd(1) }) |
| 87 | } |
| 88 | |
| 89 | pub fn stderr() -> io::Result<Handle> { |
| 90 | Handle::from_std(unsafe { File::from_raw_fd(2) }) |
| 91 | } |
| 92 | |
| 93 | pub fn as_file(&self) -> &File { |
| 94 | // unwrap() will not panic. Since we were able to open the |
| 95 | // file successfully, then `file` is guaranteed to be Some() |
| 96 | self.file.as_ref().take().unwrap() |
| 97 | } |
| 98 | |
| 99 | pub fn as_file_mut(&mut self) -> &mut File { |
| 100 | // unwrap() will not panic. Since we were able to open the |
| 101 | // file successfully, then `file` is guaranteed to be Some() |
| 102 | self.file.as_mut().take().unwrap() |
| 103 | } |
| 104 | |
| 105 | pub fn dev(&self) -> u64 { |
| 106 | self.dev |
| 107 | } |
| 108 | |
| 109 | pub fn ino(&self) -> u64 { |
| 110 | self.ino |
| 111 | } |
| 112 | } |
| 113 | |