trusty_console/detect/memory.rs
1//! `ServiceConnector` implementation for `trusty-memory`.
2//!
3//! Why: trusty-memory writes `http_addr` under its data root. The legacy
4//! dotfile path `~/.trusty-memory/http_addr` is always written regardless of
5//! platform and is the most reliable single location available without
6//! importing `trusty-memory`'s own path-resolution logic.
7//! What: `MemoryConnector` implements `detect()` using
8//! `~/.trusty-memory/http_addr`.
9//! Test: `test_memory_connector_*` in the module below. Run with
10//! `cargo test -p trusty-console`.
11
12use std::path::PathBuf;
13
14use crate::connector::{ServiceConnector, ServiceInfo};
15
16use super::helpers::detect_service;
17
18/// ServiceConnector for `trusty-memory`.
19///
20/// Why: trusty-memory writes `http_addr` under its data root. The legacy
21/// dotfile path is `~/.trusty-memory/http_addr`, which is the most reliable
22/// single location available without importing `trusty-memory`'s own
23/// path-resolution logic.
24/// What: Implements `detect()` using `~/.trusty-memory/http_addr`.
25/// Test: `test_memory_connector_with_stale_addr_file`,
26/// `test_memory_connector_no_addr_file` below.
27pub struct MemoryConnector {
28 home_dir: Option<PathBuf>,
29}
30
31impl MemoryConnector {
32 /// Create a new `MemoryConnector`.
33 ///
34 /// Why: Matches the SearchConnector pattern for consistency.
35 /// What: No-op constructor.
36 /// Test: Created in `all_connectors()`.
37 pub fn new() -> Self {
38 Self { home_dir: None }
39 }
40
41 /// Create a connector that uses `home_dir` instead of the real home.
42 ///
43 /// Why: Unit tests must not read or write the real user's `~/.trusty-*`
44 /// directories. Injecting a temp dir keeps tests hermetic.
45 /// What: Stores `home_dir` for use in `addr_file_path()`.
46 /// Test: `test_memory_connector_with_stale_addr_file`,
47 /// `test_memory_connector_no_addr_file`.
48 #[cfg(test)]
49 pub fn with_home(home_dir: PathBuf) -> Self {
50 Self {
51 home_dir: Some(home_dir),
52 }
53 }
54
55 fn addr_file_path(&self) -> PathBuf {
56 let home = self
57 .home_dir
58 .clone()
59 .or_else(dirs::home_dir)
60 .unwrap_or_else(|| PathBuf::from("/tmp"));
61 home.join(".trusty-memory").join("http_addr")
62 }
63}
64
65impl Default for MemoryConnector {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl ServiceConnector for MemoryConnector {
72 fn id(&self) -> &'static str {
73 "trusty-memory"
74 }
75
76 fn display_name(&self) -> &'static str {
77 "Trusty Memory"
78 }
79
80 /// Detect trusty-memory status.
81 ///
82 /// Why: trusty-memory writes `~/.trusty-memory/http_addr` (the legacy
83 /// dotfile path) as well as the OS data-dir path; the dotfile is always
84 /// written regardless of platform.
85 /// What: Three-step sequence: binary check → addr file + TCP probe → status.
86 /// Test: `test_memory_connector_with_stale_addr_file`,
87 /// `test_memory_connector_no_addr_file`.
88 fn detect(&self) -> ServiceInfo {
89 detect_service(
90 self.id(),
91 self.display_name(),
92 "trusty-memory",
93 self.addr_file_path(),
94 )
95 }
96}
97
98// ─── tests ────────────────────────────────────────────────────────────────────
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::connector::ServiceStatus;
104 use std::fs;
105 use tempfile::TempDir;
106
107 fn make_home_with_addr(rel_path: &str, content: &str) -> TempDir {
108 let tmp = TempDir::new().expect("tempdir");
109 let path = tmp.path().join(rel_path);
110 fs::create_dir_all(path.parent().expect("parent")).expect("mkdir");
111 fs::write(&path, content).expect("write addr file");
112 tmp
113 }
114
115 /// Why: stale addr file must yield Available (not Running) because TCP fails.
116 /// What: creates `.trusty-memory/http_addr = 127.0.0.1:14997`.
117 /// Test: this test itself.
118 #[test]
119 fn test_memory_connector_with_stale_addr_file() {
120 let tmp = make_home_with_addr(".trusty-memory/http_addr", "127.0.0.1:14997");
121 let connector = MemoryConnector::with_home(tmp.path().to_path_buf());
122 let info = connector.detect();
123 assert!(
124 info.status == ServiceStatus::Absent || info.status == ServiceStatus::Available,
125 "expected Absent or Available, got {:?}",
126 info.status
127 );
128 assert_eq!(info.id, "trusty-memory");
129 }
130
131 /// Why: absent addr file with binary on PATH yields Available; without binary
132 /// yields Absent.
133 /// What: empty temp HOME.
134 /// Test: this test itself.
135 #[test]
136 fn test_memory_connector_no_addr_file() {
137 let tmp = TempDir::new().expect("tempdir");
138 let connector = MemoryConnector::with_home(tmp.path().to_path_buf());
139 let info = connector.detect();
140 assert!(
141 info.status == ServiceStatus::Absent || info.status == ServiceStatus::Available,
142 "expected Absent or Available without addr file, got {:?}",
143 info.status
144 );
145 }
146}