use batpak::event::EventKind;
use syncbat::effect_backend::{EffectBackend, EffectError};
pub trait HostController {
fn perform(&mut self, control: &str) -> Result<(), HostControlError>;
}
impl<F> HostController for F
where
F: FnMut(&str) -> Result<(), HostControlError>,
{
fn perform(&mut self, control: &str) -> Result<(), HostControlError> {
self(control)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HostControlError {
message: String,
}
impl HostControlError {
#[must_use]
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
}
impl std::fmt::Display for HostControlError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "host control failure: {}", self.message)
}
}
impl std::error::Error for HostControlError {}
pub struct HostControlEffectBackend {
inner: Option<Box<dyn EffectBackend>>,
controller: Box<dyn HostController>,
}
impl HostControlEffectBackend {
#[must_use]
pub fn new(inner: Option<Box<dyn EffectBackend>>, controller: Box<dyn HostController>) -> Self {
Self { inner, controller }
}
}
struct NoInnerBackend;
impl EffectBackend for NoInnerBackend {
fn append_event(&mut self, _kind: EventKind, _payload: &[u8]) -> Result<(), EffectError> {
Err(EffectError::new(
"event appends are not supported without an inner effect backend",
))
}
}
impl EffectBackend for HostControlEffectBackend {
fn append_event(&mut self, kind: EventKind, payload: &[u8]) -> Result<(), EffectError> {
match self.inner.as_deref_mut() {
Some(inner) => inner.append_event(kind, payload),
None => NoInnerBackend.append_event(kind, payload),
}
}
fn read_event(&mut self, event_category: &str) -> Result<(), EffectError> {
match self.inner.as_deref_mut() {
Some(inner) => inner.read_event(event_category),
None => NoInnerBackend.read_event(event_category),
}
}
fn query_projection(&mut self, projection_id: &str) -> Result<(), EffectError> {
match self.inner.as_deref_mut() {
Some(inner) => inner.query_projection(projection_id),
None => NoInnerBackend.query_projection(projection_id),
}
}
fn emit_receipt(&mut self, receipt_kind: &str) -> Result<(), EffectError> {
match self.inner.as_deref_mut() {
Some(inner) => inner.emit_receipt(receipt_kind),
None => NoInnerBackend.emit_receipt(receipt_kind),
}
}
fn use_host_control(&mut self, control: &str) -> Result<(), EffectError> {
self.controller
.perform(control)
.map_err(|error| EffectError::new(error.to_string()))
}
}