use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct StorageType {
pub name: String,
pub repository_id: String,
pub base: Option<String>,
pub state_members: Vec<(String, String)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct StorageHome {
pub name: String,
pub repository_id: String,
pub managed_storage_type: String,
pub primary_key: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HomeExecutor {
pub name: String,
pub home_id: String,
pub component_executor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Composition {
pub name: String,
pub category: CompositionCategory,
pub home_executor: String,
pub home_storage: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum CompositionCategory {
#[default]
Session,
Service,
Process,
Entity,
}
impl Composition {
#[must_use]
pub fn requires_storage_home(&self) -> bool {
matches!(self.category, CompositionCategory::Entity)
}
pub fn validate(&self) -> Result<(), &'static str> {
if self.requires_storage_home() && self.home_storage.is_none() {
return Err("entity composition requires home_storage");
}
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn session_composition_does_not_require_storage() {
let c = Composition {
name: "TraderImpl".into(),
category: CompositionCategory::Session,
home_executor: "TraderHomeExec".into(),
home_storage: None,
};
assert!(c.validate().is_ok());
}
#[test]
fn entity_composition_without_storage_invalid() {
let c = Composition {
name: "OrderImpl".into(),
category: CompositionCategory::Entity,
home_executor: "OrderHomeExec".into(),
home_storage: None,
};
assert!(c.validate().is_err());
}
#[test]
fn entity_composition_with_storage_valid() {
let c = Composition {
name: "OrderImpl".into(),
category: CompositionCategory::Entity,
home_executor: "OrderHomeExec".into(),
home_storage: Some("OrderStorageHome".into()),
};
assert!(c.validate().is_ok());
}
#[test]
fn storage_type_can_inherit() {
let st = StorageType {
name: "OrderStorage".into(),
repository_id: "IDL:demo/OrderStorage:1.0".into(),
base: Some("BaseStorage".into()),
state_members: alloc::vec![
("id".into(), "long".into()),
("amount".into(), "double".into()),
],
};
assert!(st.base.is_some());
assert_eq!(st.state_members.len(), 2);
}
#[test]
fn home_executor_optional_component_binding() {
let he = HomeExecutor {
name: "TraderHomeExec".into(),
home_id: "IDL:demo/TraderHome:1.0".into(),
component_executor: Some("TraderExec".into()),
};
assert!(he.component_executor.is_some());
}
#[test]
fn all_four_composition_categories_distinct() {
let cats = [
CompositionCategory::Session,
CompositionCategory::Service,
CompositionCategory::Process,
CompositionCategory::Entity,
];
for (i, a) in cats.iter().enumerate() {
for b in cats.iter().skip(i + 1) {
assert_ne!(a, b);
}
}
}
}