use crate::OpenError; use normpath::PathExt; use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use std::path::PathBuf; use std::{io, ptr}; use winapi::ctypes::c_int; use winapi::um::shellapi::ShellExecuteW; #[cfg(feature = "reveal")] mod reveal; #[cfg(feature = "reveal")] pub(crate) use self::reveal::reveal; pub(crate) fn open(path: &OsStr) -> Result<(), OpenError> { let Err(first_error) = open_helper(path) else { return Ok(()); }; match PathBuf::from(path).normalize() { Ok(normalized) => match open_helper(normalized.as_os_str()) { Ok(()) => Ok(()), Err(_second_error) => Err(first_error), }, Err(_) => Err(first_error), } } pub(crate) fn open_helper(path: &OsStr) -> Result<(), OpenError> { const SW_SHOW: c_int = 5; let path = convert_path(path).map_err(OpenError::Io)?; let operation: Vec = OsStr::new("open\0").encode_wide().collect(); let result = unsafe { ShellExecuteW( ptr::null_mut(), operation.as_ptr(), path.as_ptr(), ptr::null(), ptr::null(), SW_SHOW, ) }; if result as c_int > 32 { Ok(()) } else { Err(OpenError::Io(io::Error::last_os_error())) } } fn convert_path(path: &OsStr) -> io::Result> { let mut maybe_result: Vec = path.encode_wide().collect(); if maybe_result.iter().any(|&u| u == 0) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "path contains NUL byte(s)", )); } maybe_result.push(0); Ok(maybe_result) }