use std::any::Any;
use bb_ir::component::ErasedComponent;
pub use bb_ir::component::{
ComponentPackage, ConstructError, ConstructFn, DependencyDecl, RestoreError, RestoreFn,
SerializeFn,
};
pub trait ConcreteComponent: ErasedComponent + Sized {
const TYPE_NAME: &'static str;
const PACKAGE: ComponentPackage = ComponentPackage::Application;
const DEPENDENCIES: &'static [DependencyDecl] = &[];
type Config: Any + 'static;
type Error: std::error::Error + Send + Sync + 'static;
fn new(config: &Self::Config) -> Result<Self, Self::Error>;
fn serialize(&self) -> Vec<u8>;
fn restore(bytes: &[u8]) -> Result<Self, RestoreError>;
}
pub struct ComponentHandle {
pub type_name: &'static str,
pub package: ComponentPackage,
pub instance_id: u32,
pub serialize_fn: SerializeFn,
pub restore_fn: RestoreFn,
pub state_bytes: Vec<u8>,
}
impl std::fmt::Debug for ComponentHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComponentHandle")
.field("type_name", &self.type_name)
.field("package", &self.package)
.field("instance_id", &self.instance_id)
.field("state_bytes_len", &self.state_bytes.len())
.finish_non_exhaustive()
}
}
impl ComponentHandle {
pub fn materialize(&self) -> Result<Box<dyn ErasedComponent>, RestoreError> {
(self.restore_fn)(&self.state_bytes)
}
pub fn capture_state(&self, instance: &dyn ErasedComponent) -> Vec<u8> {
(self.serialize_fn)(instance)
}
pub fn from_concrete<T: ConcreteComponent>(instance: &T, instance_id: u32) -> Self {
Self {
type_name: T::TYPE_NAME,
package: T::PACKAGE,
instance_id,
serialize_fn: |erased: &dyn ErasedComponent| -> Vec<u8> {
let any: &dyn Any = erased;
let concrete: &T = any
.downcast_ref::<T>()
.expect("ComponentHandle::serialize_fn called with mismatched type");
concrete.serialize()
},
restore_fn: |bytes: &[u8]| -> Result<Box<dyn ErasedComponent>, RestoreError> {
T::restore(bytes).map(|v| Box::new(v) as Box<dyn ErasedComponent>)
},
state_bytes: instance.serialize(),
}
}
}