use std::fs::{File, OpenOptions};
use std::io;
use std::mem;
use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::io::{
AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle,
};
use std::path::Path;
use kernel32::{GetFileInformationByHandle, GetStdHandle};
use winapi::fileapi::BY_HANDLE_FILE_INFORMATION;
use winapi::minwindef::DWORD;
use winapi::winbase::{
FILE_FLAG_BACKUP_SEMANTICS,
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
};
#[derive(Debug)]
pub struct Handle {
file: Option<File>,
is_std: bool,
key: Option<Key>,
}
#[derive(Debug, Eq, PartialEq)]
struct Key {
volume: DWORD,
idx_high: DWORD,
idx_low: DWORD,
}
impl Drop for Handle {
fn drop(&mut self) {
if self.is_std {
self.file.take().unwrap().into_raw_handle();
}
}
}
impl Eq for Handle {}
impl PartialEq for Handle {
fn eq(&self, other: &Handle) -> bool {
if self.key.is_none() || other.key.is_none() {
return false;
}
self.key == other.key
}
}
impl AsRawHandle for ::Handle {
fn as_raw_handle(&self) -> RawHandle {
self.0.file.as_ref().take().unwrap().as_raw_handle()
}
}
impl IntoRawHandle for ::Handle {
fn into_raw_handle(mut self) -> RawHandle {
self.0.file.take().unwrap().into_raw_handle()
}
}
impl Handle {
pub fn from_path<P: AsRef<Path>>(p: P) -> io::Result<Handle> {
let file = try!(OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.open(p));
Handle::from_file(file)
}
pub fn from_file(file: File) -> io::Result<Handle> {
file_info(&file).map(|info| Handle::from_file_info(file, false, info))
}
fn from_std_handle(file: File) -> io::Result<Handle> {
match file_info(&file) {
Ok(info) => Ok(Handle::from_file_info(file, true, info)),
Err(_) => Ok(Handle { file: Some(file), is_std: true, key: None }),
}
}
fn from_file_info(
file: File,
is_std: bool,
info: BY_HANDLE_FILE_INFORMATION,
) -> Handle {
Handle {
file: Some(file),
is_std: is_std,
key: Some(Key {
volume: info.dwVolumeSerialNumber,
idx_high: info.nFileIndexHigh,
idx_low: info.nFileIndexLow,
}),
}
}
pub fn stdin() -> io::Result<Handle> {
Handle::from_std_handle(unsafe {
File::from_raw_handle(GetStdHandle(STD_INPUT_HANDLE))
})
}
pub fn stdout() -> io::Result<Handle> {
Handle::from_std_handle(unsafe {
File::from_raw_handle(GetStdHandle(STD_OUTPUT_HANDLE))
})
}
pub fn stderr() -> io::Result<Handle> {
Handle::from_std_handle(unsafe {
File::from_raw_handle(GetStdHandle(STD_ERROR_HANDLE))
})
}
pub fn as_file(&self) -> &File {
self.file.as_ref().take().unwrap()
}
pub fn as_file_mut(&mut self) -> &mut File {
self.file.as_mut().take().unwrap()
}
}
fn file_info(file: &File) -> io::Result<BY_HANDLE_FILE_INFORMATION> {
let (r, info) = unsafe {
let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
(GetFileInformationByHandle(file.as_raw_handle(), &mut info), info)
};
if r == 0 {
Err(io::Error::last_os_error())
} else {
Ok(info)
}
}