adaptive_pipeline_bootstrap/platform/
unix.rs1use super::{Platform, PlatformError};
23use async_trait::async_trait;
24use std::path::{Path, PathBuf};
25
26pub struct UnixPlatform;
30
31impl UnixPlatform {
32 pub fn new() -> Self {
34 Self
35 }
36
37 #[cfg(target_os = "linux")]
39 fn get_memory_info_linux() -> Result<(u64, u64), PlatformError> {
40 use std::fs;
41
42 let meminfo = fs::read_to_string("/proc/meminfo")
43 .map_err(|e| PlatformError::Other(format!("Failed to read /proc/meminfo: {}", e)))?;
44
45 let mut total = None;
46 let mut available = None;
47
48 for line in meminfo.lines() {
49 if let Some(value) = line.strip_prefix("MemTotal:") {
50 total = value
51 .split_whitespace()
52 .next()
53 .and_then(|s| s.parse::<u64>().ok())
54 .map(|kb| kb * 1024); } else if let Some(value) = line.strip_prefix("MemAvailable:") {
56 available = value
57 .split_whitespace()
58 .next()
59 .and_then(|s| s.parse::<u64>().ok())
60 .map(|kb| kb * 1024); }
62
63 if total.is_some() && available.is_some() {
64 break;
65 }
66 }
67
68 match (total, available) {
69 (Some(t), Some(a)) => Ok((t, a)),
70 _ => Err(PlatformError::Other("Failed to parse memory info".to_string())),
71 }
72 }
73
74 #[cfg(target_os = "macos")]
76 fn get_memory_info_macos() -> Result<(u64, u64), PlatformError> {
77 use std::mem;
78
79 unsafe {
85 let mut total: u64 = 0;
87 let mut size = mem::size_of::<u64>();
88 #[rustfmt::skip]
89 let name = c"hw.memsize".as_ptr();
90
91 if libc::sysctlbyname(
92 name,
93 &mut total as *mut _ as *mut libc::c_void,
94 &mut size,
95 std::ptr::null_mut(),
96 0,
97 ) != 0
98 {
99 return Err(PlatformError::Other(
100 "Failed to get total memory via sysctl".to_string(),
101 ));
102 }
103
104 let mut available: u64 = 0;
107 let mut avail_size = mem::size_of::<u64>();
108 #[rustfmt::skip]
109 let avail_name = c"vm.page_free_count".as_ptr();
110
111 if libc::sysctlbyname(
112 avail_name,
113 &mut available as *mut _ as *mut libc::c_void,
114 &mut avail_size,
115 std::ptr::null_mut(),
116 0,
117 ) != 0
118 {
119 available = total / 2;
122 } else {
123 let page_size = Self::page_size_impl();
125 available *= page_size;
126 }
127
128 Ok((total, available))
129 }
130 }
131
132 fn page_size_impl() -> u64 {
134 unsafe {
137 let size = libc::sysconf(libc::_SC_PAGESIZE);
138 if size > 0 {
139 size as u64
140 } else {
141 4096 }
143 }
144 }
145}
146
147impl Default for UnixPlatform {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153#[async_trait]
154impl Platform for UnixPlatform {
155 fn page_size(&self) -> usize {
156 Self::page_size_impl() as usize
157 }
158
159 fn cpu_count(&self) -> usize {
160 unsafe {
163 let count = libc::sysconf(libc::_SC_NPROCESSORS_ONLN);
164 if count > 0 {
165 count as usize
166 } else {
167 1 }
169 }
170 }
171
172 fn total_memory(&self) -> Result<u64, PlatformError> {
173 #[cfg(target_os = "linux")]
174 {
175 Self::get_memory_info_linux().map(|(total, _)| total)
176 }
177
178 #[cfg(target_os = "macos")]
179 {
180 Self::get_memory_info_macos().map(|(total, _)| total)
181 }
182
183 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
184 {
185 Err(PlatformError::NotSupported(
186 "Memory info not supported on this Unix variant".to_string(),
187 ))
188 }
189 }
190
191 fn available_memory(&self) -> Result<u64, PlatformError> {
192 #[cfg(target_os = "linux")]
193 {
194 Self::get_memory_info_linux().map(|(_, available)| available)
195 }
196
197 #[cfg(target_os = "macos")]
198 {
199 Self::get_memory_info_macos().map(|(_, available)| available)
200 }
201
202 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
203 {
204 Err(PlatformError::NotSupported(
205 "Memory info not supported on this Unix variant".to_string(),
206 ))
207 }
208 }
209
210 fn line_separator(&self) -> &'static str {
211 "\n"
212 }
213
214 fn path_separator(&self) -> char {
215 ':'
216 }
217
218 fn platform_name(&self) -> &'static str {
219 #[cfg(target_os = "linux")]
220 return "linux";
221
222 #[cfg(target_os = "macos")]
223 return "macos";
224
225 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
226 return "unix";
227 }
228
229 fn temp_dir(&self) -> PathBuf {
230 std::env::temp_dir()
231 }
232
233 fn is_elevated(&self) -> bool {
234 unsafe { libc::geteuid() == 0 }
237 }
238
239 fn set_permissions(&self, path: &Path, mode: u32) -> Result<(), PlatformError> {
240 use std::fs;
241 use std::os::unix::fs::PermissionsExt;
242
243 let permissions = fs::Permissions::from_mode(mode);
244 fs::set_permissions(path, permissions)?;
245 Ok(())
246 }
247
248 fn is_executable(&self, path: &Path) -> bool {
249 use std::os::unix::fs::PermissionsExt;
250
251 if let Ok(metadata) = std::fs::metadata(path) {
252 let permissions = metadata.permissions();
253 let mode = permissions.mode();
254 (mode & 0o111) != 0
256 } else {
257 false
258 }
259 }
260
261 async fn sync_file(&self, file: &tokio::fs::File) -> Result<(), PlatformError> {
262 file.sync_all().await?;
263 Ok(())
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_unix_platform_basics() {
273 let platform = UnixPlatform::new();
274
275 assert!(platform.cpu_count() >= 1);
277
278 let page_size = platform.page_size();
280 assert!(page_size >= 512);
281 assert!(page_size <= 65536);
282 }
283
284 #[test]
285 fn test_unix_platform_constants() {
286 let platform = UnixPlatform::new();
287
288 assert_eq!(platform.line_separator(), "\n");
289 assert_eq!(platform.path_separator(), ':');
290
291 let name = platform.platform_name();
292 assert!(name == "linux" || name == "macos" || name == "unix");
293 }
294
295 #[test]
296 fn test_memory_info() {
297 let platform = UnixPlatform::new();
298
299 let total = platform.total_memory();
301 assert!(total.is_ok());
302 if let Ok(t) = total {
303 assert!(t > 0);
304 }
305
306 let available = platform.available_memory();
308 assert!(available.is_ok());
309 if let Ok(a) = available {
310 assert!(a > 0);
311 }
312 }
313
314 #[test]
315 fn test_temp_dir() {
316 let platform = UnixPlatform::new();
317 let temp = platform.temp_dir();
318 assert!(temp.exists());
319 }
320
321 #[test]
322 fn test_is_elevated() {
323 let platform = UnixPlatform::new();
324 let _ = platform.is_elevated();
326 }
327}