restart_manager/
filter.rs1use std::ffi::{OsStr, OsString};
4use std::path::{Path, PathBuf};
5
6use crate::input::{
7 absolute_user_path, validate_absolute_os_path, validate_os_output, validate_user_os_value,
8};
9use crate::{ProcessIdentity, Result};
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct FilterTarget {
18 kind: TargetKind,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22enum TargetKind {
23 Executable(PathBuf),
24 Process(ProcessIdentity),
25 Service(OsString),
26}
27
28impl FilterTarget {
29 pub fn executable(path: impl Into<PathBuf>) -> Result<Self> {
31 let path = absolute_user_path(path.into(), "an executable path")?;
32 Ok(Self {
33 kind: TargetKind::Executable(path),
34 })
35 }
36
37 #[must_use]
39 pub const fn process(process: ProcessIdentity) -> Self {
40 Self {
41 kind: TargetKind::Process(process),
42 }
43 }
44
45 pub fn service(name: impl Into<OsString>) -> Result<Self> {
47 let name = name.into();
48 validate_user_os_value(&name, "a service short name")?;
49 Ok(Self {
50 kind: TargetKind::Service(name),
51 })
52 }
53
54 #[must_use]
56 pub fn as_executable(&self) -> Option<&Path> {
57 match &self.kind {
58 TargetKind::Executable(path) => Some(path),
59 TargetKind::Process(_) | TargetKind::Service(_) => None,
60 }
61 }
62
63 #[must_use]
65 pub const fn as_process(&self) -> Option<ProcessIdentity> {
66 match self.kind {
67 TargetKind::Process(process) => Some(process),
68 TargetKind::Executable(_) | TargetKind::Service(_) => None,
69 }
70 }
71
72 #[must_use]
74 pub fn as_service(&self) -> Option<&OsStr> {
75 match &self.kind {
76 TargetKind::Service(name) => Some(name),
77 TargetKind::Executable(_) | TargetKind::Process(_) => None,
78 }
79 }
80
81 pub(crate) fn from_raw_executable(path: PathBuf) -> Result<Self> {
82 validate_absolute_os_path(&path, "executable filter path")?;
83 Ok(Self {
84 kind: TargetKind::Executable(path),
85 })
86 }
87
88 pub(crate) const fn from_raw_process(process: ProcessIdentity) -> Self {
89 Self::process(process)
90 }
91
92 pub(crate) fn from_raw_service(name: OsString) -> Result<Self> {
93 validate_os_output(&name, "service filter name")?;
94 Ok(Self {
95 kind: TargetKind::Service(name),
96 })
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub enum FilterAction {
103 PreventRestart,
105 PreventShutdown,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Hash)]
111pub struct Filter {
112 pub(crate) target: FilterTarget,
113 pub(crate) action: FilterAction,
114}
115
116impl Filter {
117 #[must_use]
119 pub const fn target(&self) -> &FilterTarget {
120 &self.target
121 }
122
123 #[must_use]
125 pub const fn action(&self) -> FilterAction {
126 self.action
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn targets_are_validated_and_disjoint() {
136 let executable = FilterTarget::executable("demo.exe").unwrap();
137 assert!(executable.as_executable().unwrap().is_absolute());
138 assert_eq!(executable.as_process(), None);
139 assert_eq!(executable.as_service(), None);
140
141 let process = ProcessIdentity::from_raw_parts(1, 2).unwrap();
142 let process_target = FilterTarget::process(process);
143 assert_eq!(process_target.as_process(), Some(process));
144
145 let service = FilterTarget::service("EventLog").unwrap();
146 assert_eq!(service.as_service(), Some(OsStr::new("EventLog")));
147 assert!(FilterTarget::service("").is_err());
148 assert!(FilterTarget::executable("").is_err());
149 assert!(FilterTarget::service("bad\0name").is_err());
150 }
151}