use std::marker::PhantomData;
use std::sync::Arc;
use crate::core::builder::ModuleBuilder;
use crate::core::capability::CapabilityKey;
use crate::core::module::Module;
use crate::kit::error::KitError;
use crate::kit::kit::Kit;
pub trait IntoKitModuleBuilder<M: Module> {
type Builder: ModuleBuilder<M>;
fn kit(self, kit: &Kit) -> KitModuleBuilder<M, Self::Builder>;
}
impl<M, B> IntoKitModuleBuilder<M> for B
where
M: Module<Builder = B>,
B: ModuleBuilder<M>,
{
type Builder = B;
fn kit(self, kit: &Kit) -> KitModuleBuilder<M, Self::Builder> {
KitModuleBuilder::new(self, kit.clone())
}
}
pub struct KitModuleBuilder<M: Module, B> {
builder: B,
kit: Kit,
_phantom: PhantomData<M>,
}
impl<M: Module, B: ModuleBuilder<M>> KitModuleBuilder<M, B> {
pub fn new(builder: B, kit: Kit) -> Self {
KitModuleBuilder {
builder,
kit,
_phantom: PhantomData,
}
}
pub fn provide<K>(self) -> Result<Arc<K::Capability>, KitError>
where
K: CapabilityKey,
M: Module<Capability = Arc<K::Capability>>,
{
let module_name = M::NAME;
let capability: Arc<K::Capability> =
self.builder.build().map_err(|e| KitError::BuildFailed {
module: module_name,
source: Box::new(e),
})?;
self.kit.provide::<K>(Arc::clone(&capability))?;
Ok(capability)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::builder::ModuleBuilder;
use crate::core::capability::CapabilityKey;
use crate::core::module::Module;
use std::fmt;
#[derive(Debug)]
struct TestError(&'static str);
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for TestError {}
struct TestCapKey;
impl CapabilityKey for TestCapKey {
type Capability = dyn Send + Sync;
const NAME: &'static str = "test_cap";
}
struct TestModule;
impl Module for TestModule {
const NAME: &'static str = "test_module";
type Config = ();
type Requirements = ();
type Capability = Arc<dyn Send + Sync>;
type Error = TestError;
type Builder = TestBuilder;
}
struct TestBuilder;
impl ModuleBuilder<TestModule> for TestBuilder {
fn build(self) -> Result<Arc<dyn Send + Sync>, TestError> {
Ok(Arc::new(42i32) as Arc<dyn Send + Sync>)
}
}
struct FailingModule;
impl Module for FailingModule {
const NAME: &'static str = "failing_module";
type Config = ();
type Requirements = ();
type Capability = Arc<dyn Send + Sync>;
type Error = TestError;
type Builder = FailingBuilder;
}
struct FailingBuilder;
impl ModuleBuilder<FailingModule> for FailingBuilder {
fn build(self) -> Result<Arc<dyn Send + Sync>, TestError> {
Err(TestError("build failed"))
}
}
#[test]
fn kit_creates_kit_module_builder() {
let kit = Kit::new();
let kmb = TestBuilder.kit(&kit);
let result = kmb.provide::<TestCapKey>();
assert!(result.is_ok());
assert!(kit.contains::<TestCapKey>());
}
#[test]
fn provide_build_failure() {
let kit = Kit::new();
let result = FailingBuilder.kit(&kit).provide::<TestCapKey>();
assert!(result.is_err(), "build should fail");
let err = result.err().unwrap();
assert!(err.to_string().contains("failed to build module"));
assert!(err.to_string().contains("failing_module"));
}
#[test]
fn provide_duplicate_failure() {
let kit = Kit::new();
TestBuilder.kit(&kit).provide::<TestCapKey>().unwrap();
let result = TestBuilder.kit(&kit).provide::<TestCapKey>();
assert!(result.is_err(), "duplicate should fail");
let err = result.err().unwrap();
assert!(err.to_string().contains("already exists"));
assert!(err.to_string().contains("test_cap"));
}
#[test]
fn test_error_display() {
let err = TestError("oops");
assert_eq!(err.to_string(), "oops");
}
}