use std::ffi::c_void;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::process::CommandExt;
use std::path::Path;
use std::process::Command;
use std::ptr::{null, null_mut};
pub use super::wincred::{secret_get, secret_remove, secret_set};
#[link(name = "bcrypt")]
unsafe extern "system" {
fn BCryptGenRandom(algorithm: *mut c_void, buffer: *mut u8, len: u32, flags: u32) -> i32;
}
#[link(name = "shell32")]
unsafe extern "system" {
fn ShellExecuteW(
window: *mut c_void,
verb: *const u16,
file: *const u16,
args: *const u16,
dir: *const u16,
show: i32,
) -> isize;
fn SHFileOperationW(operation: *mut FileOperation) -> i32;
}
#[repr(C)]
#[cfg_attr(not(target_pointer_width = "64"), repr(packed(1)))]
struct FileOperation {
window: *mut c_void,
function: u32,
from: *const u16,
to: *const u16,
flags: u16,
aborted: i32,
mappings: *mut c_void,
title: *const u16,
}
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x2;
const SW_SHOWNORMAL: i32 = 1;
const FO_DELETE: u32 = 3;
const FOF_TRASH_QUIETLY: u16 = 0x40 | 0x10 | 0x4 | 0x400;
const BELOW_NORMAL_PRIORITY_CLASS: u32 = 0x4000;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
pub(super) fn wide(text: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
text.as_ref().encode_wide().chain(std::iter::once(0)).collect()
}
pub fn fill_random(buffer: &mut [u8]) -> Result<(), String> {
let status = unsafe {
BCryptGenRandom(null_mut(), buffer.as_mut_ptr(), buffer.len() as u32, BCRYPT_USE_SYSTEM_PREFERRED_RNG)
};
if status == 0 { Ok(()) } else { Err(format!("BCryptGenRandom falló (NTSTATUS {status:#x})")) }
}
pub fn open_url(url: &str, _background: bool) -> Result<(), String> {
let (verb, file) = (wide("open"), wide(url));
let result = unsafe { ShellExecuteW(null_mut(), verb.as_ptr(), file.as_ptr(), null(), null(), SW_SHOWNORMAL) };
if result > 32 { Ok(()) } else { Err(format!("ShellExecuteW devolvió {result}")) }
}
pub fn reveal(path: &Path) -> Result<(), String> {
let mut command = Command::new("explorer");
command.raw_arg(format!("/select,\"{}\"", path.display()));
command.spawn().map(drop).map_err(|error| format!("no se pudo abrir el Explorador: {error}"))
}
pub fn trash(path: &Path) -> Result<(), String> {
let mut from = wide(path);
from.push(0);
let mut operation = FileOperation {
window: null_mut(),
function: FO_DELETE,
from: from.as_ptr(),
to: null(),
flags: FOF_TRASH_QUIETLY,
aborted: 0,
mappings: null_mut(),
title: null(),
};
match unsafe { SHFileOperationW(&mut operation) } {
0 if { operation.aborted == 0 } => Ok(()),
0 => Err("operación cancelada".into()),
code => Err(format!("SHFileOperationW devolvió {code:#x}")),
}
}
pub fn low_priority_command(program: &str) -> Command {
let mut command = Command::new(program);
command.creation_flags(BELOW_NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW);
command
}