Skip to main content

restart_manager/
resource.rs

1//! Resource collections registered with Restart Manager in one batch.
2
3use std::ffi::{OsStr, OsString};
4use std::path::{Path, PathBuf};
5
6use crate::ProcessIdentity;
7
8/// Files, processes, and services to register in one native call.
9#[derive(Debug, Clone, Default)]
10pub struct ResourceBatch {
11    files: Vec<PathBuf>,
12    processes: Vec<ProcessIdentity>,
13    services: Vec<OsString>,
14}
15
16impl ResourceBatch {
17    /// Creates an empty resource collection.
18    #[must_use]
19    pub const fn new() -> Self {
20        Self {
21            files: Vec::new(),
22            processes: Vec::new(),
23            services: Vec::new(),
24        }
25    }
26
27    /// Adds a file path and returns the collection for chaining.
28    ///
29    /// Directories are not supported. Relative paths are made absolute at
30    /// registration time; other missing paths are retained without
31    /// canonicalizing or resolving symlinks.
32    #[must_use]
33    pub fn file(mut self, file: impl Into<PathBuf>) -> Self {
34        self.files.push(file.into());
35        self
36    }
37
38    /// Adds a process and returns the collection for chaining.
39    #[must_use]
40    pub fn process(mut self, process: ProcessIdentity) -> Self {
41        self.processes.push(process);
42        self
43    }
44
45    /// Adds a service short name and returns the collection for chaining.
46    #[must_use]
47    pub fn service(mut self, service: impl Into<OsString>) -> Self {
48        self.services.push(service.into());
49        self
50    }
51
52    /// Adds a file path in place.
53    pub fn add_file(&mut self, file: impl Into<PathBuf>) -> &mut Self {
54        self.files.push(file.into());
55        self
56    }
57
58    /// Adds a process in place.
59    pub fn add_process(&mut self, process: ProcessIdentity) -> &mut Self {
60        self.processes.push(process);
61        self
62    }
63
64    /// Adds a service short name in place.
65    pub fn add_service(&mut self, service: impl Into<OsString>) -> &mut Self {
66        self.services.push(service.into());
67        self
68    }
69
70    /// Returns whether no resources have been added.
71    #[must_use]
72    pub fn is_empty(&self) -> bool {
73        self.files.is_empty() && self.processes.is_empty() && self.services.is_empty()
74    }
75
76    /// Returns the number of resources across all three categories.
77    #[must_use]
78    pub fn len(&self) -> usize {
79        self.files.len() + self.processes.len() + self.services.len()
80    }
81
82    pub(crate) fn files(&self) -> impl Iterator<Item = &Path> {
83        self.files.iter().map(PathBuf::as_path)
84    }
85
86    pub(crate) fn processes(&self) -> &[ProcessIdentity] {
87        &self.processes
88    }
89
90    pub(crate) fn services(&self) -> impl Iterator<Item = &OsStr> {
91        self.services.iter().map(OsString::as_os_str)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn builder_and_mutating_forms_cover_every_resource_kind() {
101        let process = ProcessIdentity::from_raw_parts(1, 2).unwrap();
102        let resources = ResourceBatch::new()
103            .file("one.txt")
104            .process(process)
105            .service("EventLog");
106        assert_eq!(resources.len(), 3);
107        assert!(!resources.is_empty());
108        assert_eq!(
109            resources.files().collect::<Vec<_>>(),
110            [Path::new("one.txt")]
111        );
112        assert_eq!(resources.processes(), &[process]);
113        assert_eq!(
114            resources.services().collect::<Vec<_>>(),
115            [OsStr::new("EventLog")]
116        );
117
118        let mut resources = ResourceBatch::default();
119        assert!(resources.is_empty());
120        resources
121            .add_file("two.txt")
122            .add_process(process)
123            .add_service("Schedule");
124        assert_eq!(resources.len(), 3);
125    }
126}