use crate::hw_probe::ScanBackend;
use super::CompiledScanner;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SelectedGpuDispatchError {
reason: String,
}
impl SelectedGpuDispatchError {
pub(crate) fn new(reason: impl Into<String>) -> Self {
Self {
reason: reason.into(),
}
}
pub(crate) fn reason(&self) -> &str {
&self.reason
}
}
impl std::fmt::Display for SelectedGpuDispatchError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.reason)
}
}
impl std::error::Error for SelectedGpuDispatchError {}
#[must_use]
pub(crate) fn gpu_forced_unavailable_message(
scanner: &CompiledScanner,
backend: ScanBackend,
) -> Option<String> {
if !backend.is_gpu() {
return None;
}
if scanner.gpu_stack_usable_for(backend) {
return None;
}
Some(format!(
"{} selected but GPU stack unavailable ({}, gpu_literals={}, gpu_matcher={}) - \
silent CPU fallback is forbidden; repair this GPU driver and recalibrate autoroute, or explicitly choose `--backend simd-regex` or `--backend cpu-fallback`",
backend.label(),
scanner.gpu_backend_unavailable_reason(backend),
scanner.gpu_literals.is_some(),
scanner.gpu_matcher().is_some(),
))
}
pub(crate) fn require_selected_gpu_stack(scanner: &CompiledScanner, backend: ScanBackend) {
if let Some(msg) = gpu_forced_unavailable_message(scanner, backend) {
crate::process_exit::require_gpu_unmet(msg);
}
}
pub(crate) fn fail_selected_gpu_dispatch(scanner: &CompiledScanner, reason: &str) -> ! {
fail_selected_gpu_dispatch_error(scanner, SelectedGpuDispatchError::new(reason))
}
pub(crate) fn fail_selected_gpu_dispatch_error(
scanner: &CompiledScanner,
error: SelectedGpuDispatchError,
) -> ! {
scanner.record_gpu_runtime_fault(error.reason());
crate::process_exit::require_gpu_unmet(format!(
"selected GPU dispatch failed at runtime ({error}) \
(literals={}, backend={}, matcher={}); refusing to substitute CPU/SIMD. \
Run `keyhog backend --self-test`, then recalibrate autoroute or select another backend explicitly",
scanner.gpu_literals.is_some(),
scanner.gpu_backends.availability().any(),
scanner.gpu_matcher().is_some(),
));
}