cargo_reclaim/active_process/
sysinfo_provider.rs1use std::path::Path;
2
3use crate::planner::{ActiveObservation, ObservedCargoProcess};
4
5use super::common::{detect_tool, extract_referenced_paths, process_matches_scope};
6use super::foundation::{ActiveObservationProvider, ActiveObservationScope};
7
8#[derive(Debug, Clone, Default)]
9pub struct SysinfoActiveObservationProvider;
10
11impl ActiveObservationProvider for SysinfoActiveObservationProvider {
12 fn observe(&self, scope: &ActiveObservationScope) -> ActiveObservation {
13 let processes = observe_sysinfo_processes(scope);
14 ActiveObservation::complete(processes)
15 }
16}
17
18fn observe_sysinfo_processes(scope: &ActiveObservationScope) -> Vec<ObservedCargoProcess> {
19 use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System, UpdateKind};
20
21 let mut system = System::new();
22 system.refresh_processes_specifics(
23 ProcessesToUpdate::All,
24 true,
25 ProcessRefreshKind::nothing()
26 .with_cmd(UpdateKind::Always)
27 .with_cwd(UpdateKind::Always),
28 );
29
30 system
31 .processes()
32 .values()
33 .filter_map(|process| {
34 let name = process.name().to_string_lossy();
35 let cmdline = process
36 .cmd()
37 .iter()
38 .map(|arg| arg.to_string_lossy().into_owned())
39 .collect::<Vec<_>>();
40 observed_process_from_parts(&name, &cmdline, process.cwd())
41 })
42 .filter(|process| process_matches_scope(process, scope))
43 .collect()
44}
45
46fn observed_process_from_parts(
47 name: &str,
48 cmdline: &[String],
49 cwd: Option<&Path>,
50) -> Option<ObservedCargoProcess> {
51 let tool = detect_tool(name, cmdline)?;
52 let mut process = ObservedCargoProcess::new(tool);
53 if let Some(cwd) = cwd {
54 process.cwd = Some(cwd.to_path_buf());
55 }
56 process.referenced_paths = extract_referenced_paths(cmdline, cwd);
57 Some(process)
58}
59
60#[cfg(test)]
61mod tests {
62 use std::path::PathBuf;
63
64 use crate::planner::{CargoTool, TargetContext};
65
66 use super::*;
67
68 #[test]
69 fn sysinfo_parts_match_cargo_by_cwd_scope() {
70 let scope = ActiveObservationScope::from_target_contexts([TargetContext::new(
71 "/work/crate/target",
72 )
73 .with_project_root("/work/crate")]);
74
75 let process = observed_process_from_parts(
76 "cargo",
77 &["cargo".to_string(), "test".to_string()],
78 Some(Path::new("/work/crate")),
79 )
80 .expect("cargo should be detected");
81
82 assert_eq!(process.tool, CargoTool::Cargo);
83 assert!(process_matches_scope(&process, &scope));
84 }
85
86 #[test]
87 fn sysinfo_parts_match_rustc_by_referenced_path_scope() {
88 let scope = ActiveObservationScope::from_target_contexts([TargetContext::new(
89 "/work/crate/target",
90 )
91 .with_build_root("/work/crate/target/debug")]);
92
93 let process = observed_process_from_parts(
94 "rustc",
95 &[
96 "rustc".to_string(),
97 "--out-dir".to_string(),
98 "target/debug/deps".to_string(),
99 ],
100 Some(Path::new("/work/crate")),
101 )
102 .expect("rustc should be detected");
103
104 assert_eq!(process.tool, CargoTool::Rustc);
105 assert_eq!(
106 process.referenced_paths,
107 vec![PathBuf::from("/work/crate/target/debug/deps")]
108 );
109 assert!(process_matches_scope(&process, &scope));
110 }
111
112 #[test]
113 fn sysinfo_parts_ignore_unrelated_processes() {
114 assert!(observed_process_from_parts("bash", &["bash".to_string()], None).is_none());
115 }
116}