gglib_core/ports/system_probe.rs
1//! System probe port for dependency and GPU detection.
2//!
3//! This port abstracts active system probing (command execution, hardware detection)
4//! from the core domain. Implementations live in adapters (e.g., gglib-runtime).
5//!
6//! # Design Notes
7//!
8//! - Core owns the trait and types (pure)
9//! - Runtime owns the implementation (active probing via `Command::new`)
10//! - CLI injects the probe via main.rs
11
12use crate::utils::system::{Dependency, GpuInfo, SystemMemoryInfo};
13use thiserror::Error;
14
15/// Errors that can occur during system probing.
16#[derive(Debug, Error)]
17pub enum SystemProbeError {
18 /// Failed to execute a command for dependency checking.
19 #[error("Command execution failed: {0}")]
20 CommandFailed(String),
21
22 /// Failed to parse version output.
23 #[error("Version parse failed for {command}: {reason}")]
24 VersionParseFailed { command: String, reason: String },
25
26 /// GPU detection failed.
27 #[error("GPU detection failed: {0}")]
28 GpuDetectionFailed(String),
29
30 /// System memory query failed.
31 #[error("Memory query failed: {0}")]
32 MemoryQueryFailed(String),
33}
34
35/// Result type for system probe operations.
36pub type SystemProbeResult<T> = Result<T, SystemProbeError>;
37
38/// Port for probing system dependencies and hardware.
39///
40/// Implementations of this trait perform active system probing by executing
41/// commands, querying hardware, etc. The core domain uses this trait to
42/// remain pure and testable.
43///
44/// # Example
45///
46/// ```ignore
47/// use gglib_core::ports::SystemProbePort;
48///
49/// fn check_system(probe: &dyn SystemProbePort) {
50/// let deps = probe.check_all_dependencies();
51/// let gpu = probe.detect_gpu_info();
52/// // ...
53/// }
54/// ```
55pub trait SystemProbePort: Send + Sync {
56 /// Check all system dependencies and return their status.
57 ///
58 /// Returns a list of dependencies with their installation status,
59 /// version information, and hints for installation.
60 fn check_all_dependencies(&self) -> Vec<Dependency>;
61
62 /// Detect GPU hardware and acceleration software.
63 ///
64 /// Returns information about available GPUs including NVIDIA/CUDA,
65 /// AMD/ROCm, and Apple Metal support.
66 fn detect_gpu_info(&self) -> GpuInfo;
67
68 /// Get system memory information for model fit calculations.
69 ///
70 /// Returns total RAM, GPU memory (if available), and platform info
71 /// useful for determining which models can run on this system.
72 fn get_system_memory_info(&self) -> SystemMemoryInfo;
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::utils::system::DependencyStatus;
79
80 /// Mock implementation for testing.
81 struct MockSystemProbe {
82 deps: Vec<Dependency>,
83 gpu: GpuInfo,
84 memory: SystemMemoryInfo,
85 }
86
87 impl SystemProbePort for MockSystemProbe {
88 fn check_all_dependencies(&self) -> Vec<Dependency> {
89 self.deps.clone()
90 }
91
92 fn detect_gpu_info(&self) -> GpuInfo {
93 self.gpu.clone()
94 }
95
96 fn get_system_memory_info(&self) -> SystemMemoryInfo {
97 self.memory.clone()
98 }
99 }
100
101 #[test]
102 fn test_mock_probe() {
103 let probe = MockSystemProbe {
104 deps: vec![
105 Dependency::required("cargo", "Rust build tool").with_status(
106 DependencyStatus::Present {
107 version: "1.75.0".to_string(),
108 },
109 ),
110 ],
111 gpu: GpuInfo {
112 has_nvidia_gpu: false,
113 cuda_version: None,
114 has_metal: true,
115 },
116 memory: SystemMemoryInfo {
117 total_ram_bytes: 16 * 1024 * 1024 * 1024,
118 gpu_memory_bytes: Some(12 * 1024 * 1024 * 1024),
119 is_apple_silicon: true,
120 has_nvidia_gpu: false,
121 },
122 };
123
124 let deps = probe.check_all_dependencies();
125 assert_eq!(deps.len(), 1);
126 assert_eq!(deps[0].name, "cargo");
127
128 let gpu = probe.detect_gpu_info();
129 assert!(gpu.has_metal);
130 assert!(!gpu.has_nvidia_gpu);
131
132 let mem = probe.get_system_memory_info();
133 assert!(mem.is_apple_silicon);
134 }
135}