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