rtlsdr-pure 0.1.1

Pure Rust RTL-SDR driver using cross_usb for native USB and WebUSB
Documentation
use std::fmt;

use crate::TunerKind;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    Usb(cross_usb::usb::Error),
    #[cfg(target_family = "wasm")]
    WebUsb(String),
    DeviceNotFound,
    TunerNotFound,
    UnsupportedTuner(TunerKind),
    InvalidSampleRate(u32),
    InvalidFrequency(u32),
    InvalidGain(i32),
    InvalidTransferLength(usize),
    ShortTransfer {
        expected: usize,
        actual: usize,
    },
    PllDidNotLock,
    ArithmeticOverflow,
    ControlTransfer {
        operation: &'static str,
        value: u16,
        index: u16,
        source: Box<Error>,
    },
    Operation {
        operation: &'static str,
        source: Box<Error>,
    },
}

impl From<cross_usb::usb::Error> for Error {
    fn from(value: cross_usb::usb::Error) -> Self {
        Self::Usb(value)
    }
}

#[cfg(target_family = "wasm")]
impl From<js_sys::Error> for Error {
    fn from(value: js_sys::Error) -> Self {
        Self::WebUsb(value.message().into())
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Usb(cross_usb::usb::Error::CommunicationError(message)) => {
                write!(f, "USB communication failed: {message}")
            }
            Self::Usb(err) => write!(f, "USB error: {err}"),
            #[cfg(target_family = "wasm")]
            Self::WebUsb(message) => write!(f, "WebUSB error: {message}"),
            Self::DeviceNotFound => f.write_str("no known RTL-SDR device found"),
            Self::TunerNotFound => f.write_str("no supported tuner was detected"),
            Self::UnsupportedTuner(tuner) => write!(f, "unsupported tuner: {tuner:?}"),
            Self::InvalidSampleRate(rate) => write!(f, "invalid RTL2832 sample rate: {rate} Hz"),
            Self::InvalidFrequency(freq) => write!(f, "invalid frequency: {freq} Hz"),
            Self::InvalidGain(gain) => write!(f, "invalid tuner gain: {gain} tenths of a dB"),
            Self::InvalidTransferLength(len) => {
                write!(f, "USB control transfer length is too large: {len}")
            }
            Self::ShortTransfer { expected, actual } => {
                write!(
                    f,
                    "short USB transfer: expected {expected} bytes, got {actual}"
                )
            }
            Self::PllDidNotLock => f.write_str("R82xx tuner PLL did not lock"),
            Self::ArithmeticOverflow => f.write_str("integer overflow while configuring device"),
            Self::ControlTransfer {
                operation,
                value,
                index,
                source,
            } => write!(
                f,
                "{operation} failed (value=0x{value:04x}, index=0x{index:04x}): {source}"
            ),
            Self::Operation { operation, source } => {
                write!(f, "{operation} failed: {source}")
            }
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Usb(err) => Some(err),
            Self::ControlTransfer { source, .. } | Self::Operation { source, .. } => Some(source),
            _ => None,
        }
    }
}

impl Error {
    pub(crate) fn context(self, operation: &'static str) -> Self {
        Self::Operation {
            operation,
            source: Box::new(self),
        }
    }

    pub(crate) fn control(self, operation: &'static str, value: u16, index: u16) -> Self {
        Self::ControlTransfer {
            operation,
            value,
            index,
            source: Box::new(self),
        }
    }
}

pub(crate) trait ResultExt<T> {
    fn context(self, operation: &'static str) -> Result<T>;
}

impl<T> ResultExt<T> for Result<T> {
    fn context(self, operation: &'static str) -> Result<T> {
        self.map_err(|err| err.context(operation))
    }
}