ai_hwaccel/error.rs
1//! Detection error types.
2//!
3//! Detection is best-effort: errors are collected as warnings rather than
4//! aborting. Callers can inspect them to understand why a backend was skipped.
5
6use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10/// An error encountered during hardware detection.
11///
12/// These are non-fatal — detection continues even when individual backends
13/// fail. The registry collects them as [`crate::AcceleratorRegistry::warnings`].
14///
15/// # Examples
16///
17/// ```rust
18/// use ai_hwaccel::DetectionError;
19///
20/// let err = DetectionError::ToolNotFound { tool: "nvidia-smi".into() };
21/// assert!(err.to_string().contains("nvidia-smi"));
22///
23/// let err = DetectionError::Timeout { tool: "hl-smi".into(), timeout_secs: 5.0 };
24/// assert!(err.to_string().contains("timed out"));
25/// ```
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27#[non_exhaustive]
28pub enum DetectionError {
29 /// A required CLI tool was not found on `$PATH`.
30 ToolNotFound { tool: String },
31
32 /// A CLI tool was found but exited with a non-zero status.
33 ToolFailed {
34 tool: String,
35 exit_code: Option<i32>,
36 stderr: String,
37 },
38
39 /// A CLI tool did not exit within the allowed timeout.
40 ///
41 /// The process was killed. This is distinct from [`ToolFailed`](Self::ToolFailed)
42 /// to allow callers to implement retry logic for transient slowness.
43 Timeout { tool: String, timeout_secs: f64 },
44
45 /// Output from a CLI tool or sysfs file could not be parsed.
46 ParseError { backend: String, message: String },
47
48 /// A sysfs or procfs path could not be read.
49 SysfsReadError { path: String, message: String },
50}
51
52impl fmt::Display for DetectionError {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 Self::ToolNotFound { tool } => {
56 write!(f, "{}: tool not found on $PATH", tool)
57 }
58 Self::ToolFailed {
59 tool,
60 exit_code,
61 stderr,
62 } => {
63 write!(
64 f,
65 "{}: exited with code {} — {}",
66 tool,
67 exit_code
68 .map(|c| c.to_string())
69 .unwrap_or_else(|| "signal".into()),
70 stderr.lines().next().unwrap_or("(no output)")
71 )
72 }
73 Self::Timeout { tool, timeout_secs } => {
74 write!(f, "{}: timed out after {:.1}s", tool, timeout_secs)
75 }
76 Self::ParseError { backend, message } => {
77 write!(f, "{}: parse error — {}", backend, message)
78 }
79 Self::SysfsReadError { path, message } => {
80 write!(f, "sysfs {}: {}", path, message)
81 }
82 }
83 }
84}
85
86impl std::error::Error for DetectionError {}