use std::fmt;
use crate::ChannelCount;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceDescription {
name: String,
manufacturer: Option<String>,
driver: Option<String>,
device_type: DeviceType,
interface_type: InterfaceType,
direction: DeviceDirection,
address: Option<String>,
extended: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum DeviceType {
Speaker,
Microphone,
Headphones,
Headset,
Earpiece,
Handset,
HearingAid,
Dock,
Tuner,
Virtual,
#[default]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum InterfaceType {
BuiltIn,
Usb,
Bluetooth,
Pci,
FireWire,
Thunderbolt,
Hdmi,
Line,
Spdif,
Network,
Virtual,
DisplayPort,
Aggregate,
#[default]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum DeviceDirection {
Input,
Output,
Duplex,
#[default]
Unknown,
}
impl DeviceDescription {
pub fn name(&self) -> &str {
&self.name
}
pub fn manufacturer(&self) -> Option<&str> {
self.manufacturer.as_deref()
}
pub fn driver(&self) -> Option<&str> {
self.driver.as_deref()
}
pub fn device_type(&self) -> DeviceType {
self.device_type
}
pub fn interface_type(&self) -> InterfaceType {
self.interface_type
}
pub fn direction(&self) -> DeviceDirection {
self.direction
}
pub fn supports_input(&self) -> bool {
matches!(
self.direction,
DeviceDirection::Input | DeviceDirection::Duplex
)
}
pub fn supports_output(&self) -> bool {
matches!(
self.direction,
DeviceDirection::Output | DeviceDirection::Duplex
)
}
pub fn address(&self) -> Option<&str> {
self.address.as_deref()
}
pub fn extended(&self) -> &[String] {
&self.extended
}
}
impl fmt::Display for DeviceDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(mfr) = &self.manufacturer {
write!(f, " ({})", mfr)?;
}
if self.device_type != DeviceType::Unknown {
write!(f, " [{}]", self.device_type)?;
}
if self.interface_type != InterfaceType::Unknown {
write!(f, " via {}", self.interface_type)?;
}
Ok(())
}
}
impl fmt::Display for DeviceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeviceType::Speaker => write!(f, "Speaker"),
DeviceType::Microphone => write!(f, "Microphone"),
DeviceType::Headphones => write!(f, "Headphones"),
DeviceType::Headset => write!(f, "Headset"),
DeviceType::Earpiece => write!(f, "Earpiece"),
DeviceType::Handset => write!(f, "Handset"),
DeviceType::HearingAid => write!(f, "Hearing Aid"),
DeviceType::Dock => write!(f, "Dock"),
DeviceType::Tuner => write!(f, "Tuner"),
DeviceType::Virtual => write!(f, "Virtual"),
_ => write!(f, "Unknown"),
}
}
}
impl fmt::Display for InterfaceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InterfaceType::BuiltIn => write!(f, "Built-in"),
InterfaceType::Usb => write!(f, "USB"),
InterfaceType::Bluetooth => write!(f, "Bluetooth"),
InterfaceType::Pci => write!(f, "PCI"),
InterfaceType::FireWire => write!(f, "FireWire"),
InterfaceType::Thunderbolt => write!(f, "Thunderbolt"),
InterfaceType::Hdmi => write!(f, "HDMI"),
InterfaceType::Line => write!(f, "Line"),
InterfaceType::Spdif => write!(f, "S/PDIF"),
InterfaceType::Network => write!(f, "Network"),
InterfaceType::Virtual => write!(f, "Virtual"),
InterfaceType::DisplayPort => write!(f, "DisplayPort"),
InterfaceType::Aggregate => write!(f, "Aggregate"),
_ => write!(f, "Unknown"),
}
}
}
impl fmt::Display for DeviceDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeviceDirection::Input => write!(f, "Input"),
DeviceDirection::Output => write!(f, "Output"),
DeviceDirection::Duplex => write!(f, "Duplex"),
_ => write!(f, "Unknown"),
}
}
}
#[derive(Debug, Clone)]
pub struct DeviceDescriptionBuilder {
name: String,
manufacturer: Option<String>,
driver: Option<String>,
device_type: DeviceType,
interface_type: InterfaceType,
direction: DeviceDirection,
address: Option<String>,
extended: Vec<String>,
}
impl DeviceDescriptionBuilder {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
manufacturer: None,
driver: None,
device_type: DeviceType::default(),
interface_type: InterfaceType::default(),
direction: DeviceDirection::default(),
address: None,
extended: Vec::new(),
}
}
pub fn manufacturer(mut self, manufacturer: impl Into<String>) -> Self {
self.manufacturer = Some(manufacturer.into());
self
}
pub fn driver(mut self, driver: impl Into<String>) -> Self {
self.driver = Some(driver.into());
self
}
pub fn device_type(mut self, device_type: DeviceType) -> Self {
self.device_type = device_type;
self
}
pub fn interface_type(mut self, interface_type: InterfaceType) -> Self {
self.interface_type = interface_type;
self
}
pub fn direction(mut self, direction: DeviceDirection) -> Self {
self.direction = direction;
self
}
pub fn address(mut self, address: impl Into<String>) -> Self {
self.address = Some(address.into());
self
}
pub fn extended(mut self, lines: Vec<String>) -> Self {
self.extended = lines;
self
}
pub fn add_extended_line(mut self, line: impl Into<String>) -> Self {
self.extended.push(line.into());
self
}
pub fn build(self) -> DeviceDescription {
DeviceDescription {
name: self.name,
manufacturer: self.manufacturer,
driver: self.driver,
device_type: self.device_type,
interface_type: self.interface_type,
direction: self.direction,
address: self.address,
extended: self.extended,
}
}
}
pub(crate) fn direction_from_caps(has_input: bool, has_output: bool) -> DeviceDirection {
match (has_input, has_output) {
(true, true) => DeviceDirection::Duplex,
(true, false) => DeviceDirection::Input,
(false, true) => DeviceDirection::Output,
(false, false) => DeviceDirection::Unknown,
}
}
#[allow(dead_code)]
pub(crate) fn direction_from_counts(
input_channels: Option<ChannelCount>,
output_channels: Option<ChannelCount>,
) -> DeviceDirection {
let has_input = input_channels.map(|n| n > 0).unwrap_or(false);
let has_output = output_channels.map(|n| n > 0).unwrap_or(false);
direction_from_caps(has_input, has_output)
}