use std::{collections::BTreeMap, sync::Arc};
use sim_kernel::{Cx, Error, Expr, Result, Symbol};
use crate::{
HostBackend, HostDeviceInventory, HostOpenPlan, HostOpenStream, HostStreamConfigRequest,
missing_capability_card_expr,
};
#[derive(Default)]
pub struct HostBackendRegistry {
backends: BTreeMap<Symbol, Arc<dyn HostBackend>>,
}
impl HostBackendRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register<B>(&mut self, backend: B) -> Result<()>
where
B: HostBackend + 'static,
{
self.register_arc(Arc::new(backend))
}
pub fn register_arc(&mut self, backend: Arc<dyn HostBackend>) -> Result<()> {
let id = backend.info().id().clone();
if self.backends.contains_key(&id) {
return Err(Error::Eval(format!(
"stream host backend {id} is already registered"
)));
}
self.backends.insert(id, backend);
Ok(())
}
pub fn backend(&self, id: &Symbol) -> Option<Arc<dyn HostBackend>> {
self.backends.get(id).cloned()
}
pub fn enumerate(&self) -> Result<Vec<HostDeviceInventory>> {
self.backends
.values()
.map(|backend| backend.enumerate())
.collect()
}
pub fn plan_open(&self, request: &HostStreamConfigRequest) -> Result<HostOpenPlan> {
let backend = self.backend_or_error(request.backend())?;
let inventory = backend.enumerate()?;
let device = inventory
.devices()
.iter()
.find(|device| device.id() == request.device())
.ok_or_else(|| {
Error::Eval(format!(
"stream host backend {} has no device {}",
request.backend(),
request.device()
))
})?;
if device.media() != request.media() {
return Err(Error::TypeMismatch {
expected: "request media matching host device",
found: "request media for another host device",
});
}
if device.direction() != request.direction() {
return Err(Error::TypeMismatch {
expected: "request direction matching host device",
found: "request direction for another host device",
});
}
Ok(device.open_plan())
}
pub fn open_checked(
&self,
cx: &mut Cx,
request: HostStreamConfigRequest,
) -> Result<HostOpenStream> {
self.plan_open(&request)?.enforce(cx)?;
self.open(request)
}
pub fn open(&self, request: HostStreamConfigRequest) -> Result<HostOpenStream> {
self.plan_open(&request)?;
let backend = self.backend_or_error(request.backend())?;
backend.open(request)
}
fn backend_or_error(&self, backend: &Symbol) -> Result<Arc<dyn HostBackend>> {
self.backends
.get(backend)
.cloned()
.ok_or_else(|| Error::Eval(format!("stream host backend {backend} is not registered")))
}
pub fn card_exprs(&self) -> Result<Vec<Expr>> {
let mut cards = Vec::new();
for backend in self.backends.values() {
cards.push(backend.info().card_expr());
cards.extend(backend.enumerate()?.card_exprs());
}
Ok(cards)
}
pub fn missing_capability_card(
&self,
backend: &Symbol,
capability: crate::HostBackendCapability,
) -> Expr {
missing_capability_card_expr(backend, capability)
}
}