Skip to main content

entrenar/gpu/
error.rs

1//! GPU sharing error types (GPU-SHARE-001/002/003).
2
3use std::fmt;
4
5/// Errors from GPU resource management.
6#[derive(Debug)]
7pub enum GpuError {
8    /// Not enough VRAM for requested budget.
9    InsufficientMemory {
10        budget_mb: usize,
11        available_mb: usize,
12        reserved_mb: usize,
13        total_mb: usize,
14    },
15    /// Wait-for-VRAM timed out.
16    Timeout { budget_mb: usize, timeout_secs: u64 },
17    /// Ledger file is corrupt or unreadable.
18    LedgerCorrupt(String),
19    /// I/O error accessing ledger.
20    Io(std::io::Error),
21}
22
23impl fmt::Display for GpuError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            Self::InsufficientMemory { budget_mb, available_mb, reserved_mb, total_mb } => {
27                write!(
28                    f,
29                    "insufficient VRAM: need {budget_mb} MB, available {available_mb} MB \
30                     (reserved {reserved_mb} MB / total {total_mb} MB)"
31                )
32            }
33            Self::Timeout { budget_mb, timeout_secs } => {
34                write!(f, "VRAM wait timed out after {timeout_secs}s (need {budget_mb} MB)")
35            }
36            Self::LedgerCorrupt(msg) => write!(f, "ledger corrupt: {msg}"),
37            Self::Io(e) => write!(f, "GPU ledger I/O: {e}"),
38        }
39    }
40}
41
42impl std::error::Error for GpuError {
43    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
44        match self {
45            Self::Io(e) => Some(e),
46            _ => None,
47        }
48    }
49}
50
51impl From<std::io::Error> for GpuError {
52    fn from(e: std::io::Error) -> Self {
53        Self::Io(e)
54    }
55}
56
57#[cfg(test)]
58#[allow(clippy::unwrap_used)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn test_gpu_error_insufficient_memory_display() {
64        let err = GpuError::InsufficientMemory {
65            budget_mb: 8000,
66            available_mb: 4000,
67            reserved_mb: 12000,
68            total_mb: 16000,
69        };
70        let msg = format!("{err}");
71        assert!(msg.contains("8000"));
72        assert!(msg.contains("4000"));
73        assert!(msg.contains("12000"));
74        assert!(msg.contains("16000"));
75    }
76
77    #[test]
78    fn test_gpu_error_timeout_display() {
79        let err = GpuError::Timeout { budget_mb: 4000, timeout_secs: 30 };
80        let msg = format!("{err}");
81        assert!(msg.contains("30"));
82        assert!(msg.contains("4000"));
83    }
84
85    #[test]
86    fn test_gpu_error_ledger_corrupt_display() {
87        let err = GpuError::LedgerCorrupt("bad checksum".into());
88        let msg = format!("{err}");
89        assert!(msg.contains("bad checksum"));
90    }
91
92    #[test]
93    fn test_gpu_error_io_display() {
94        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
95        let err = GpuError::Io(io_err);
96        let msg = format!("{err}");
97        assert!(msg.contains("file missing"));
98    }
99
100    #[test]
101    fn test_gpu_error_source_io() {
102        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
103        let err = GpuError::Io(io_err);
104        assert!(std::error::Error::source(&err).is_some());
105    }
106
107    #[test]
108    fn test_gpu_error_source_non_io() {
109        let err = GpuError::Timeout { budget_mb: 100, timeout_secs: 5 };
110        assert!(std::error::Error::source(&err).is_none());
111
112        let err = GpuError::LedgerCorrupt("test".into());
113        assert!(std::error::Error::source(&err).is_none());
114
115        let err = GpuError::InsufficientMemory {
116            budget_mb: 1,
117            available_mb: 0,
118            reserved_mb: 1,
119            total_mb: 1,
120        };
121        assert!(std::error::Error::source(&err).is_none());
122    }
123
124    #[test]
125    fn test_gpu_error_from_io() {
126        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "no access");
127        let err: GpuError = io_err.into();
128        match err {
129            GpuError::Io(e) => assert_eq!(e.kind(), std::io::ErrorKind::PermissionDenied),
130            other => panic!("Expected Io variant, got: {other:?}"),
131        }
132    }
133
134    #[test]
135    fn test_gpu_error_debug() {
136        let err = GpuError::Timeout { budget_mb: 100, timeout_secs: 5 };
137        let debug = format!("{err:?}");
138        assert!(debug.contains("Timeout"));
139    }
140}