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}