use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LifecyclePhase {
PostConstruct,
PreDestroy,
PrePassivate,
PostActivate,
}
pub trait LifecycleCallback {
fn on_phase(&mut self, phase: LifecyclePhase) -> Result<(), &'static str>;
}
pub struct ConnectorBean {
pub name: String,
pub component_id: String,
callback: Box<dyn LifecycleCallback + Send>,
history: Vec<LifecyclePhase>,
}
impl core::fmt::Debug for ConnectorBean {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ConnectorBean")
.field("name", &self.name)
.field("component_id", &self.component_id)
.field("history", &self.history)
.finish_non_exhaustive()
}
}
impl ConnectorBean {
pub fn new<C>(name: String, component_id: String, callback: C) -> Self
where
C: LifecycleCallback + Send + 'static,
{
Self {
name,
component_id,
callback: Box::new(callback),
history: Vec::new(),
}
}
pub fn enter_phase(&mut self, phase: LifecyclePhase) -> Result<(), &'static str> {
self.callback.on_phase(phase)?;
self.history.push(phase);
Ok(())
}
#[must_use]
pub fn history(&self) -> &[LifecyclePhase] {
&self.history
}
}
#[cfg(feature = "std")]
pub fn pss_session_for_bean<'b>(
bean: &'b ConnectorBean,
pss: &zerodds_corba_ccm::pss::PssSession,
) -> PssBeanBinding<'b> {
PssBeanBinding {
bean_name: &bean.name,
component_id: &bean.component_id,
tx_status: pss.tx_status(),
}
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PssBeanBinding<'b> {
pub bean_name: &'b str,
pub component_id: &'b str,
pub tx_status: zerodds_corba_ccm::pss::PssTxStatus,
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
struct Counter {
post_construct: u32,
pre_destroy: u32,
}
impl LifecycleCallback for Counter {
fn on_phase(&mut self, phase: LifecyclePhase) -> Result<(), &'static str> {
match phase {
LifecyclePhase::PostConstruct => self.post_construct += 1,
LifecyclePhase::PreDestroy => self.pre_destroy += 1,
_ => {}
}
Ok(())
}
}
#[test]
fn post_construct_invokes_callback() {
let mut bean = ConnectorBean::new(
"EchoBean".into(),
"IDL:demo/Echo:1.0".into(),
Counter {
post_construct: 0,
pre_destroy: 0,
},
);
bean.enter_phase(LifecyclePhase::PostConstruct).unwrap();
assert_eq!(bean.history(), &[LifecyclePhase::PostConstruct]);
}
#[test]
fn full_lifecycle_records_history() {
let mut bean = ConnectorBean::new(
"EchoBean".into(),
"IDL:demo/Echo:1.0".into(),
Counter {
post_construct: 0,
pre_destroy: 0,
},
);
bean.enter_phase(LifecyclePhase::PostConstruct).unwrap();
bean.enter_phase(LifecyclePhase::PrePassivate).unwrap();
bean.enter_phase(LifecyclePhase::PostActivate).unwrap();
bean.enter_phase(LifecyclePhase::PreDestroy).unwrap();
assert_eq!(bean.history().len(), 4);
assert_eq!(bean.history()[0], LifecyclePhase::PostConstruct);
assert_eq!(bean.history()[3], LifecyclePhase::PreDestroy);
}
#[cfg(feature = "std")]
#[test]
fn connector_bean_with_pss_session_binds_correctly() {
use alloc::sync::Arc;
use zerodds_corba_ccm::pss::{InMemoryStorageHome, PssSession, PssTxStatus, StorageHome};
let bean = ConnectorBean::new(
"EchoBean".into(),
"IDL:demo/Echo:1.0".into(),
Counter {
post_construct: 0,
pre_destroy: 0,
},
);
let home = Arc::new(InMemoryStorageHome::new()) as Arc<dyn StorageHome>;
let pss = PssSession::new(home);
let binding = pss_session_for_bean(&bean, &pss);
assert_eq!(binding.bean_name, "EchoBean");
assert_eq!(binding.component_id, "IDL:demo/Echo:1.0");
assert_eq!(binding.tx_status, PssTxStatus::NoTransaction);
let _tx = pss.begin_transaction().expect("begin");
let active_binding = pss_session_for_bean(&bean, &pss);
assert_eq!(active_binding.tx_status, PssTxStatus::Active);
}
#[test]
fn callback_failure_propagates() {
struct Failing;
impl LifecycleCallback for Failing {
fn on_phase(&mut self, _: LifecyclePhase) -> Result<(), &'static str> {
Err("boom")
}
}
let mut bean = ConnectorBean::new("X".into(), "IDL:x:1.0".into(), Failing);
assert!(bean.enter_phase(LifecyclePhase::PostConstruct).is_err());
assert!(bean.history().is_empty());
}
}