use std::fmt;
use sim_kernel::{Expr, Symbol};
pub type DeviceResult<T> = std::result::Result<T, DeviceError>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeviceError {
Unsupported,
Sample(String),
Host(String),
}
impl fmt::Display for DeviceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unsupported => f.write_str("device provider is unsupported"),
Self::Sample(message) => write!(f, "device sample error: {message}"),
Self::Host(message) => f.write_str(message),
}
}
}
impl std::error::Error for DeviceError {}
impl From<DeviceError> for sim_kernel::Error {
fn from(error: DeviceError) -> Self {
match error {
DeviceError::Unsupported => {
Self::HostError("device provider is unsupported".to_owned())
}
DeviceError::Sample(message) => Self::Eval(format!("device sample error: {message}")),
DeviceError::Host(message) => Self::HostError(message),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeviceProfile {
pub device: Symbol,
pub streams: Vec<Symbol>,
pub inputs: Vec<Symbol>,
pub outputs: Vec<Symbol>,
pub sample_kinds: Vec<Symbol>,
}
impl DeviceProfile {
pub fn new(
device: Symbol,
streams: Vec<Symbol>,
inputs: Vec<Symbol>,
outputs: Vec<Symbol>,
sample_kinds: Vec<Symbol>,
) -> Self {
Self {
device,
streams,
inputs,
outputs,
sample_kinds,
}
}
pub fn modeled_edge() -> Self {
Self::new(
Symbol::qualified("device", "modeled-edge"),
vec![
Symbol::qualified("device/stream", "battery"),
Symbol::qualified("device/stream", "motion"),
],
vec![Symbol::qualified("device/input", "button")],
vec![
Symbol::qualified("device/output", "screen"),
Symbol::qualified("device/output", "haptic"),
],
vec![device_sample_kind_symbol("device-caps")],
)
}
pub fn supports_sample_kind(&self, sample_kind: &Symbol) -> bool {
self.sample_kinds.contains(sample_kind)
}
}
pub trait DeviceSample: Sized {
fn sample_kind() -> &'static str;
fn to_expr(&self) -> Expr;
fn from_expr(expr: &Expr) -> DeviceResult<Self>;
}
pub fn device_sample_kind_symbol(kind: &str) -> Symbol {
Symbol::qualified("stream/device-sample", kind)
}
pub trait DeviceProvider: Send {
fn open(&self) -> DeviceResult<Box<dyn DeviceSession>>;
}
pub trait DeviceSession: Send {
fn profile(&self) -> &DeviceProfile;
fn start(&mut self) -> DeviceResult<()>;
fn poll(&mut self, kind: &str) -> DeviceResult<Option<Expr>>;
fn send(&mut self, command: &Expr) -> DeviceResult<()>;
fn stop(&mut self) -> DeviceResult<()>;
}
pub fn poll_device_sample<S>(session: &mut dyn DeviceSession) -> DeviceResult<Option<S>>
where
S: DeviceSample,
{
session
.poll(S::sample_kind())?
.map(|expr| S::from_expr(&expr))
.transpose()
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StubProvider {
profile: DeviceProfile,
}
impl StubProvider {
pub fn new(profile: DeviceProfile) -> Self {
Self { profile }
}
pub fn profile(&self) -> &DeviceProfile {
&self.profile
}
pub fn session(&self) -> StubSession {
StubSession::new(self.profile.clone())
}
}
impl DeviceProvider for StubProvider {
fn open(&self) -> DeviceResult<Box<dyn DeviceSession>> {
Err(DeviceError::Unsupported)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StubSession {
profile: DeviceProfile,
}
impl StubSession {
pub fn new(profile: DeviceProfile) -> Self {
Self { profile }
}
}
impl DeviceSession for StubSession {
fn profile(&self) -> &DeviceProfile {
&self.profile
}
fn start(&mut self) -> DeviceResult<()> {
Err(DeviceError::Unsupported)
}
fn poll(&mut self, _kind: &str) -> DeviceResult<Option<Expr>> {
Err(DeviceError::Unsupported)
}
fn send(&mut self, _command: &Expr) -> DeviceResult<()> {
Err(DeviceError::Unsupported)
}
fn stop(&mut self) -> DeviceResult<()> {
Ok(())
}
}