Skip to main content

baracuda_core/
error.rs

1//! Error types shared across the baracuda crates.
2
3use std::path::PathBuf;
4
5use baracuda_types::{CudaStatus, CudaVersion};
6use thiserror::Error;
7
8/// An error raised by the dynamic loader.
9///
10/// These surface whenever an NVIDIA shared library or one of its symbols
11/// cannot be resolved at runtime — typically because CUDA is not installed,
12/// the installed driver is older than what baracuda was built against, or
13/// the user is on a platform NVIDIA doesn't support.
14///
15/// `#[non_exhaustive]` — new loader failure modes may land as CUDA
16/// adds entry points (`cuGetProcAddress` v2, the per-library minor-
17/// version checks). Match arms must include a `_ =>` catch-all.
18#[derive(Debug, Error)]
19#[non_exhaustive]
20pub enum LoaderError {
21    /// None of the candidate library filenames resolved anywhere on the
22    /// library search path.
23    #[error("could not load {library}: tried {candidates:?} across {search_paths} path(s)")]
24    LibraryNotFound {
25        /// Value field.
26        library: &'static str,
27        /// Value field.
28        candidates: Vec<&'static str>,
29        /// Value field.
30        search_paths: usize,
31    },
32
33    /// The library was loaded but did not export the requested symbol.
34    #[error("library '{library}' is missing symbol '{symbol}'")]
35    SymbolNotFound {
36        /// Value field.
37        library: &'static str,
38        /// Value field.
39        symbol: &'static str,
40    },
41
42    /// `cuGetProcAddress` returned `CU_GET_PROC_ADDRESS_VERSION_NOT_SUFFICIENT`
43    /// for `symbol`: the installed driver does not provide it at the version
44    /// baracuda asked for.
45    #[error("symbol '{symbol}' requires {required} but baracuda's driver loader sees {installed}")]
46    VersionTooOld {
47        /// Value field.
48        symbol: &'static str,
49        /// Value field.
50        required: CudaVersion,
51        /// Value field.
52        installed: CudaVersion,
53    },
54
55    /// Raw `libloading` error — kept for platform-specific diagnostics that
56    /// the other variants can't express (e.g. a missing dependency on a
57    /// chained `.so`).
58    #[error("{0}")]
59    Libloading(#[from] libloading::Error),
60
61    /// baracuda does not target this platform (e.g. macOS).
62    #[error(
63        "baracuda does not support {platform}; NVIDIA driver is only available on Linux and Windows"
64    )]
65    UnsupportedPlatform {
66        /// Name of the unsupported platform (e.g. "macOS").
67        platform: &'static str,
68    },
69
70    /// An env-var override (e.g. `BARACUDA_NVRTC_PATH`) was set but the
71    /// library could not be loaded from the path it names. Returned instead
72    /// of silently falling back to the hardcoded candidates, since a set
73    /// override that silently failed would hide exactly the misconfiguration
74    /// the override exists to let the caller pin down.
75    #[error("{env_var}={path} is set but {library} failed to load from it: {source}")]
76    EnvOverrideUnusable {
77        /// The library's logical name (e.g. `"nvrtc"`).
78        library: &'static str,
79        /// The env var that was set (e.g. `"BARACUDA_NVRTC_PATH"`).
80        env_var: &'static str,
81        /// The path the env var named.
82        path: PathBuf,
83        /// Why loading from that path failed.
84        source: Box<LoaderError>,
85    },
86}
87
88impl LoaderError {
89    /// Convenience constructor for the common case of "tried these names,
90    /// none worked".
91    pub fn library_not_found(library: &'static str, candidates: &[&'static str]) -> Self {
92        Self::LibraryNotFound {
93            library,
94            candidates: candidates.to_vec(),
95            search_paths: 0,
96        }
97    }
98
99    /// As above, but records how many directories were searched.
100    pub fn library_not_found_with_search(
101        library: &'static str,
102        candidates: &[&'static str],
103        search_path_count: usize,
104    ) -> Self {
105        Self::LibraryNotFound {
106            library,
107            candidates: candidates.to_vec(),
108            search_paths: search_path_count,
109        }
110    }
111}
112
113/// A generic error enum for any safe wrapper crate over a single NVIDIA
114/// library. Safe crates may use this directly or compose their own richer
115/// `Error` enum out of its variants.
116///
117/// `#[non_exhaustive]` — new error variants may land as new failure modes
118/// are surfaced by NVIDIA libraries. Match arms must include a `_ =>`
119/// catch-all.
120#[derive(Debug, Error)]
121#[non_exhaustive]
122pub enum Error<S>
123where
124    S: CudaStatus + Send + Sync + 'static,
125{
126    /// The library returned a non-success status code.
127    #[error("{} returned {} ({}): {}", .status.library(), .status.name(), .status.code(), .status.description())]
128    Status {
129        /// The non-success status code returned by the underlying library.
130        status: S,
131    },
132
133    /// The dynamic loader failed.
134    #[error(transparent)]
135    Loader(#[from] LoaderError),
136
137    /// The requested API is newer than the installed driver supports.
138    #[error("{api} requires {since}; install a newer driver to use it")]
139    FeatureNotSupported {
140        /// Value field.
141        api: &'static str,
142        /// Value field.
143        since: CudaVersion,
144    },
145}
146
147impl<S> Error<S>
148where
149    S: CudaStatus + Send + Sync + 'static,
150{
151    /// Treat a raw status code as a `Result`. Success codes yield `Ok(())`,
152    /// all others yield `Err(Error::Status { .. })`.
153    pub fn check(status: S) -> Result<(), Self> {
154        if status.is_success() {
155            Ok(())
156        } else {
157            Err(Self::Status { status })
158        }
159    }
160}
161
162/// A library-erased error, useful at process boundaries where the caller
163/// doesn't want to parameterize over every NVIDIA library's status enum.
164///
165/// `#[non_exhaustive]` — new error categories may land as the workspace
166/// adds backends. Match arms must include a `_ =>` catch-all.
167#[derive(Debug, Error)]
168#[non_exhaustive]
169pub enum BaracudaError {
170    /// A status code from any NVIDIA library.
171    #[error("{library} returned {name} ({code}): {description}")]
172    Status {
173        /// Value field.
174        library: &'static str,
175        /// Value field.
176        name: &'static str,
177        /// Value field.
178        description: &'static str,
179        /// Value field.
180        code: i32,
181    },
182
183    /// The dynamic loader failed.
184    #[error(transparent)]
185    Loader(#[from] LoaderError),
186
187    /// The requested API is newer than the installed driver supports.
188    #[error("{api} requires {since}; install a newer driver to use it")]
189    FeatureNotSupported {
190        /// Value field.
191        api: &'static str,
192        /// Value field.
193        since: CudaVersion,
194    },
195
196    /// For sources that want to attach a path or other context (e.g. a
197    /// missing PTX file).
198    #[error("{context}")]
199    Context {
200        /// Free-form context attached by the failing call site.
201        context: &'static str,
202    },
203}
204
205impl<S> From<Error<S>> for BaracudaError
206where
207    S: CudaStatus + Send + Sync + 'static,
208{
209    fn from(err: Error<S>) -> Self {
210        match err {
211            Error::Status { status } => BaracudaError::Status {
212                library: status.library(),
213                name: status.name(),
214                description: status.description(),
215                code: status.code(),
216            },
217            Error::Loader(l) => BaracudaError::Loader(l),
218            Error::FeatureNotSupported { api, since } => {
219                BaracudaError::FeatureNotSupported { api, since }
220            }
221        }
222    }
223}
224
225/// Path-returning variant used by `find_library` probes. (Kept out of public
226/// API surface for now — re-exported here so doc-links work.)
227#[allow(dead_code)]
228pub(crate) type PathList = Vec<PathBuf>;