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