use std::ffi;
use std::os::windows::ffi::{
OsStrExt,
OsStringExt
};
use core::{default, ptr, mem, convert};
use crate::sys::*;
use crate::utils::{self, Result};
const NO_MORE_FILES: i32 = ERROR_NO_MORE_FILES as i32;
pub enum FileInfoLevel {
Standard,
Basic,
Max
}
impl convert::Into<FINDEX_INFO_LEVELS> for FileInfoLevel {
fn into(self) -> FINDEX_INFO_LEVELS {
match self {
FileInfoLevel::Standard => FindExInfoStandard,
FileInfoLevel::Basic => FindExInfoBasic,
FileInfoLevel::Max => FindExInfoMaxInfoLevel
}
}
}
impl default::Default for FileInfoLevel {
fn default() -> Self {
FileInfoLevel::Standard
}
}
pub enum FileSearchType {
NameMatch,
DirectoriesOnly
}
impl convert::Into<FINDEX_SEARCH_OPS> for FileSearchType {
fn into(self) -> FINDEX_SEARCH_OPS {
match self {
FileSearchType::NameMatch => FindExSearchNameMatch,
FileSearchType::DirectoriesOnly => FindExSearchLimitToDirectories
}
}
}
impl default::Default for FileSearchType {
fn default() -> Self {
FileSearchType::NameMatch
}
}
pub struct Entry(WIN32_FIND_DATAW);
impl Entry {
pub fn is_dir(&self) -> bool {
(self.0.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
}
pub fn is_file(&self) -> bool {
!self.is_dir()
}
pub fn size(&self) -> u64 {
((self.0.nFileSizeHigh as u64) << 32) | (self.0.nFileSizeLow as u64)
}
pub fn is_read_only(&self) -> bool {
(self.0.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0
}
pub fn name(&self) -> ffi::OsString {
ffi::OsString::from_wide(match self.0.cFileName.iter().position(|c| *c == 0) {
Some(n) => &self.0.cFileName[..n],
None => &self.0.cFileName
})
}
}
pub struct Search(HANDLE);
impl Search {
pub fn new<T: ?Sized + AsRef<ffi::OsStr>>(name: &T, level: FileInfoLevel, typ: FileSearchType, flags: DWORD) -> Result<(Search, Entry)> {
let mut utf16_buff: Vec<u16> = name.as_ref().encode_wide().collect();
utf16_buff.push(0);
let mut file_data: WIN32_FIND_DATAW = unsafe { mem::zeroed() };
let result = unsafe {
FindFirstFileExW(utf16_buff.as_ptr(),
level.into(),
&mut file_data as *mut _ as *mut c_void,
typ.into(),
ptr::null_mut(),
flags)
};
if result == INVALID_HANDLE_VALUE {
Err(utils::get_last_error())
}
else {
Ok((Search(result), Entry(file_data)))
}
}
pub fn again(&self) -> Result<Entry> {
let mut file_data: WIN32_FIND_DATAW = unsafe { mem::zeroed() };
unsafe {
if FindNextFileW(self.0, &mut file_data) != 0 {
Ok(Entry(file_data))
}
else {
Err(utils::get_last_error())
}
}
}
pub fn close(self) {
unsafe {
FindClose(self.0);
}
}
}
impl Iterator for Search {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
match self.again() {
Ok(data) => Some(Ok(data)),
Err(error) => {
match error.raw_code() {
NO_MORE_FILES => None,
_ => Some(Err(error))
}
}
}
}
}
impl Drop for Search {
fn drop(&mut self) {
unsafe {
debug_assert!(FindClose(self.0) != 0);
}
}
}