1use std::fmt;
2
3use crate::TunerKind;
4
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Debug)]
8pub enum Error {
9 Usb(nusb::Error),
10 UsbTransfer(nusb::transfer::TransferError),
11 #[cfg(target_family = "wasm")]
12 WebUsb(String),
13 DeviceNotFound,
14 TunerNotFound,
15 UnsupportedTuner(TunerKind),
16 InvalidSampleRate(u32),
17 InvalidFrequency(u32),
18 InvalidGain(i32),
19 InvalidTransferLength(usize),
20 ShortTransfer {
21 expected: usize,
22 actual: usize,
23 },
24 PllDidNotLock,
25 ArithmeticOverflow,
26 ControlTransfer {
27 operation: &'static str,
28 value: u16,
29 index: u16,
30 source: Box<Error>,
31 },
32 Operation {
33 operation: &'static str,
34 source: Box<Error>,
35 },
36}
37
38impl From<nusb::Error> for Error {
39 fn from(value: nusb::Error) -> Self {
40 Self::Usb(value)
41 }
42}
43
44impl From<nusb::transfer::TransferError> for Error {
45 fn from(value: nusb::transfer::TransferError) -> Self {
46 Self::UsbTransfer(value)
47 }
48}
49
50#[cfg(target_family = "wasm")]
51impl From<Error> for wasm_bindgen::JsValue {
52 fn from(e: Error) -> Self {
53 wasm_bindgen::JsValue::from_str(&format!("rtlsdr_pure: {e}"))
54 }
55}
56
57#[cfg(target_family = "wasm")]
58impl From<js_sys::Error> for Error {
59 fn from(value: js_sys::Error) -> Self {
60 Self::WebUsb(value.message().into())
61 }
62}
63
64impl fmt::Display for Error {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 match self {
67 Self::Usb(err) => write!(f, "USB error: {err}"),
68 Self::UsbTransfer(err) => write!(f, "USB transfer failed: {err}"),
69 #[cfg(target_family = "wasm")]
70 Self::WebUsb(message) => write!(f, "WebUSB error: {message}"),
71 Self::DeviceNotFound => f.write_str("no known RTL-SDR device found"),
72 Self::TunerNotFound => f.write_str("no supported tuner was detected"),
73 Self::UnsupportedTuner(tuner) => write!(f, "unsupported tuner: {tuner:?}"),
74 Self::InvalidSampleRate(rate) => write!(f, "invalid RTL2832 sample rate: {rate} Hz"),
75 Self::InvalidFrequency(freq) => write!(f, "invalid frequency: {freq} Hz"),
76 Self::InvalidGain(gain) => write!(f, "invalid tuner gain: {gain} tenths of a dB"),
77 Self::InvalidTransferLength(len) => {
78 write!(f, "USB control transfer length is too large: {len}")
79 }
80 Self::ShortTransfer { expected, actual } => {
81 write!(
82 f,
83 "short USB transfer: expected {expected} bytes, got {actual}"
84 )
85 }
86 Self::PllDidNotLock => f.write_str("R82xx tuner PLL did not lock"),
87 Self::ArithmeticOverflow => f.write_str("integer overflow while configuring device"),
88 Self::ControlTransfer {
89 operation,
90 value,
91 index,
92 source,
93 } => write!(
94 f,
95 "{operation} failed (value=0x{value:04x}, index=0x{index:04x}): {source}"
96 ),
97 Self::Operation { operation, source } => {
98 write!(f, "{operation} failed: {source}")
99 }
100 }
101 }
102}
103
104impl std::error::Error for Error {
105 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
106 match self {
107 Self::Usb(err) => Some(err),
108 Self::UsbTransfer(err) => Some(err),
109 Self::ControlTransfer { source, .. } | Self::Operation { source, .. } => Some(source),
110 _ => None,
111 }
112 }
113}
114
115impl Error {
116 pub(crate) fn context(self, operation: &'static str) -> Self {
117 Self::Operation {
118 operation,
119 source: Box::new(self),
120 }
121 }
122
123 pub(crate) fn control(self, operation: &'static str, value: u16, index: u16) -> Self {
124 Self::ControlTransfer {
125 operation,
126 value,
127 index,
128 source: Box::new(self),
129 }
130 }
131}
132
133pub(crate) trait ResultExt<T> {
134 fn context(self, operation: &'static str) -> Result<T>;
135}
136
137impl<T> ResultExt<T> for Result<T> {
138 fn context(self, operation: &'static str) -> Result<T> {
139 self.map_err(|err| err.context(operation))
140 }
141}