use std::collections::HashMap;
use std::sync::Arc;
use rill_core::io::{IoCapture, IoDriver, IoPlayback};
use rill_core::traits::ParamValue;
pub type BackendParts = (
Arc<dyn IoDriver>,
Option<Arc<dyn IoCapture>>,
Option<Arc<dyn IoPlayback>>,
);
pub type BackendCtor = fn(params: &HashMap<String, ParamValue>) -> Result<BackendParts, String>;
pub struct OutputBundle {
pub driver: Arc<dyn IoDriver>,
pub playback: Arc<dyn IoPlayback>,
}
pub struct InputBundle {
pub driver: Arc<dyn IoDriver>,
pub capture: Arc<dyn IoCapture>,
}
pub struct DuplexBundle {
pub driver: Arc<dyn IoDriver>,
pub capture: Arc<dyn IoCapture>,
pub playback: Arc<dyn IoPlayback>,
}
#[derive(Clone)]
pub struct BackendFactory {
ctors: HashMap<&'static str, BackendCtor>,
cache: HashMap<String, BackendParts>,
}
impl BackendFactory {
pub fn new() -> Self {
Self {
ctors: HashMap::new(),
cache: HashMap::new(),
}
}
pub fn register(&mut self, name: &'static str, ctor: BackendCtor) {
self.ctors.insert(name, ctor);
}
fn get_or_create(
&mut self,
name: &str,
params: &HashMap<String, ParamValue>,
) -> Result<BackendParts, String> {
if let Some(cached) = self.cache.get(name) {
return Ok(cached.clone());
}
let ctor = self
.ctors
.get(name)
.ok_or_else(|| format!("unknown backend: {name}"))?;
let result = ctor(params)?;
self.cache.insert(name.to_string(), result.clone());
Ok(result)
}
pub fn create_any(
&mut self,
name: &str,
params: &HashMap<String, ParamValue>,
) -> Result<BackendParts, String> {
self.get_or_create(name, params)
}
pub fn create_output(
&mut self,
name: &str,
params: &HashMap<String, ParamValue>,
) -> Result<OutputBundle, String> {
let (driver, _capture, playback) = self.get_or_create(name, params)?;
Ok(OutputBundle {
driver,
playback: playback
.ok_or_else(|| format!("backend '{name}' does not support output"))?,
})
}
pub fn create_input(
&mut self,
name: &str,
params: &HashMap<String, ParamValue>,
) -> Result<InputBundle, String> {
let (driver, capture, _playback) = self.get_or_create(name, params)?;
Ok(InputBundle {
driver,
capture: capture.ok_or_else(|| format!("backend '{name}' does not support input"))?,
})
}
pub fn create_duplex(
&mut self,
name: &str,
params: &HashMap<String, ParamValue>,
) -> Result<DuplexBundle, String> {
let (driver, capture, playback) = self.get_or_create(name, params)?;
Ok(DuplexBundle {
driver,
capture: capture.ok_or_else(|| format!("backend '{name}' does not support input"))?,
playback: playback
.ok_or_else(|| format!("backend '{name}' does not support output"))?,
})
}
pub fn contains(&self, name: &str) -> bool {
self.ctors.contains_key(name)
}
}
impl Default for BackendFactory {
fn default() -> Self {
Self::new()
}
}