use std::{env, path::PathBuf};
use e_utils::system::cmd;
use crate::{
drive::DriveNodeInfo,
input::Opts,
share::{default_cmd_res, CmdRes, SYSTEM_WIN32},
};
use super::{DriveInfo, DriveStatus, DriveStatusType};
pub const EDRIVE_EXE: &[u8] = include_bytes!("../../dist/windows/devcon.exe");
pub const EDRIVE_NAME: &'static str = "e-drive.exe";
pub fn get_drive_path() -> PathBuf {
env::temp_dir().to_path_buf()
}
pub fn devcon_parse_driver_nodes(info: DriveInfo, node_data: &str) -> Vec<DriveNodeInfo> {
let mut drive_info_list = Vec::new();
let mut info = DriveNodeInfo::from(info);
for _line in node_data.lines() {
let line = _line.trim_start_matches('\x20');
if let Some(value) = line.strip_prefix("Name: ") {
info.name = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver node #") {
info.drive_node = value.to_string();
} else if let Some(value) = line.strip_prefix("Inf file is ") {
info.inf_file = value.to_string();
} else if let Some(value) = line.strip_prefix("Inf section is ") {
info.inf_section = value.to_string();
} else if let Some(value) = line.strip_prefix("Manufacturer name is ") {
info.manufacturer_name = value.to_string();
} else if let Some(value) = line.strip_prefix("Provider name is ") {
info.provider_name = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver date is ") {
info.driver_date = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver version is ") {
info.driver_version = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver node rank is ") {
info.driver_node_rank = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver node flags are ") {
info.driver_node_flags = value.to_string();
info.signed = line.contains("digitally signed");
drive_info_list.push(info.clone());
}
}
drive_info_list
}
pub fn devcon_parse_driver_class(output: &str) -> Vec<DriveInfo> {
let mut nline = vec![];
for line in output.lines() {
if line.contains(':') {
if let Some((k, v)) = line.split_once(':') {
let id = k.trim().to_string();
let driver_descript = v.trim().to_string();
nline.push(DriveInfo {
id,
driver_descript,
});
}
}
}
nline
}
pub fn devcon(command: Vec<&str>) -> CmdRes {
let mut outres = default_cmd_res();
let args = command;
match cmd(EDRIVE_NAME, args, Some(get_drive_path()), false, true) {
Ok(output) => {
let output = output.stdout;
outres.content = output;
outres.status = true;
}
Err(e) => outres.content = e.to_string(),
}
outres
}
pub fn pnputil(command: Vec<&str>) -> CmdRes {
let mut outres = default_cmd_res();
let args = command;
match cmd("PNPUTIL", args, Some(SYSTEM_WIN32.into()), false, true) {
Ok(output) => {
let output = output.stdout;
outres.content = output;
outres.status = true;
}
Err(e) => outres.content = e.to_string(),
}
outres
}
pub fn pnputil_enable(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/enable-device"];
args.extend(commands);
pnputil(args)
}
pub fn pnputil_disable(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/disable-device"];
args.extend(commands);
pnputil(args)
}
pub fn pnputil_remove(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/remove-device"];
args.extend(commands);
pnputil(args)
}
pub fn pnputil_restart(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/restart-device"];
args.extend(commands);
pnputil(args)
}
pub fn pnputil_add_driver(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/add-driver"];
args.extend(commands);
pnputil(args)
}
pub fn devcon_status(id: &str) -> DriveStatus {
let res = devcon(vec!["status", &format!("@{id}")]);
let mut status = DriveStatus::default();
status.id = id.to_string();
let status_f = |v: &str| -> DriveStatusType {
if v.contains("disable") {
DriveStatusType::Disabled
} else if v.contains("running") {
DriveStatusType::Runing
} else {
DriveStatusType::None
}
};
if res.content.contains("matching device(s) found") {
for _line in res.content.lines() {
let line = _line.trim();
if let Some(value) = line.strip_prefix("Name: ") {
status.name = value.to_string();
} else if let Some(value) = line.strip_prefix("Driver is ") {
status.status = status_f(value);
break;
} else if let Some(value) = line.strip_prefix("Device is ") {
status.status = status_f(value);
break;
}
}
}
status.content = res.content;
status
}
pub fn pnputil_scan() -> CmdRes {
pnputil(vec!["/scan-devices"])
}
pub fn pnputil_delete_driver(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/delete-driver"];
args.extend(commands);
pnputil(args)
}
pub fn pnputil_export_driver(commands: Vec<&str>) -> CmdRes {
let mut args = vec!["/export-driver"];
args.extend(commands);
pnputil(args)
}
pub fn find_with_run<F>(opts: &Opts, f: F) -> Vec<DriveStatus>
where
F: Fn(Vec<&str>) -> CmdRes,
{
let commands = opts
.command
.iter()
.map(|x| x.as_ref())
.collect::<Vec<&str>>();
let devcon_node_list = findnodes(opts, commands);
let args_src: Vec<&str> = opts.args.iter().map(|x| x.as_ref()).collect();
let mut res_list = vec![];
for devcon_node in &devcon_node_list {
let mut args = args_src.clone();
args.insert(0, &devcon_node.id);
let _fres = f(args);
let status = devcon_status(&devcon_node.id);
res_list.push(status);
}
res_list
}
pub fn findnodes(opts: &Opts, commands: Vec<&str>) -> Vec<DriveNodeInfo> {
let mut args = vec!["findall"];
args.extend(commands);
let res = devcon(args);
let devcon_class = devcon_parse_driver_class(&res.content);
let mut devcon_node_list = vec![];
if opts.full {
for dclass in &devcon_class {
if is_filter(&dclass.driver_descript, &opts.filter) {
let devcon_node = devcon_drive_node(dclass.clone());
if devcon_node.len() > 0 {
devcon_node_list.extend(devcon_node);
} else {
devcon_node_list.push(DriveNodeInfo::from(dclass.clone()));
}
}
}
} else {
devcon_node_list = devcon_class
.into_iter()
.map(|x| DriveNodeInfo::from(x))
.collect();
}
devcon_node_list
}
pub fn devcon_drive_node(info: DriveInfo) -> Vec<DriveNodeInfo> {
let node_res = devcon(vec!["drivernodes", &format!("@{}", info.id)]);
let devcon_node = devcon_parse_driver_nodes(info, &node_res.content);
devcon_node
}
pub fn is_filter(data: &str, filters: &Vec<String>) -> bool {
if filters.len() == 0 {
return true;
}
for f in filters {
if data.contains(f) {
return true;
}
}
false
}