use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use crate::input::{
absolute_user_path, validate_absolute_os_path, validate_os_output, validate_user_os_value,
};
use crate::{ProcessIdentity, Result};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FilterTarget {
kind: TargetKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum TargetKind {
Executable(PathBuf),
Process(ProcessIdentity),
Service(OsString),
}
impl FilterTarget {
pub fn executable(path: impl Into<PathBuf>) -> Result<Self> {
let path = absolute_user_path(path.into(), "an executable path")?;
Ok(Self {
kind: TargetKind::Executable(path),
})
}
#[must_use]
pub const fn process(process: ProcessIdentity) -> Self {
Self {
kind: TargetKind::Process(process),
}
}
pub fn service(name: impl Into<OsString>) -> Result<Self> {
let name = name.into();
validate_user_os_value(&name, "a service short name")?;
Ok(Self {
kind: TargetKind::Service(name),
})
}
#[must_use]
pub fn as_executable(&self) -> Option<&Path> {
match &self.kind {
TargetKind::Executable(path) => Some(path),
TargetKind::Process(_) | TargetKind::Service(_) => None,
}
}
#[must_use]
pub const fn as_process(&self) -> Option<ProcessIdentity> {
match self.kind {
TargetKind::Process(process) => Some(process),
TargetKind::Executable(_) | TargetKind::Service(_) => None,
}
}
#[must_use]
pub fn as_service(&self) -> Option<&OsStr> {
match &self.kind {
TargetKind::Service(name) => Some(name),
TargetKind::Executable(_) | TargetKind::Process(_) => None,
}
}
pub(crate) fn from_raw_executable(path: PathBuf) -> Result<Self> {
validate_absolute_os_path(&path, "executable filter path")?;
Ok(Self {
kind: TargetKind::Executable(path),
})
}
pub(crate) const fn from_raw_process(process: ProcessIdentity) -> Self {
Self::process(process)
}
pub(crate) fn from_raw_service(name: OsString) -> Result<Self> {
validate_os_output(&name, "service filter name")?;
Ok(Self {
kind: TargetKind::Service(name),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FilterAction {
PreventRestart,
PreventShutdown,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Filter {
pub(crate) target: FilterTarget,
pub(crate) action: FilterAction,
}
impl Filter {
#[must_use]
pub const fn target(&self) -> &FilterTarget {
&self.target
}
#[must_use]
pub const fn action(&self) -> FilterAction {
self.action
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn targets_are_validated_and_disjoint() {
let executable = FilterTarget::executable("demo.exe").unwrap();
assert!(executable.as_executable().unwrap().is_absolute());
assert_eq!(executable.as_process(), None);
assert_eq!(executable.as_service(), None);
let process = ProcessIdentity::from_raw_parts(1, 2).unwrap();
let process_target = FilterTarget::process(process);
assert_eq!(process_target.as_process(), Some(process));
let service = FilterTarget::service("EventLog").unwrap();
assert_eq!(service.as_service(), Some(OsStr::new("EventLog")));
assert!(FilterTarget::service("").is_err());
assert!(FilterTarget::executable("").is_err());
assert!(FilterTarget::service("bad\0name").is_err());
}
}