pub trait Registration: 'static + Send + Sync {
type Instance;
type Metadata;
fn name(&self) -> &str;
fn version(&self) -> &str;
fn create(&self) -> Self::Instance;
fn metadata(&self) -> Self::Metadata;
}
#[macro_export]
macro_rules! define_registration {
($name:ident, $instance:ty, $metadata:ty) => {
#[derive(Debug, Clone, Copy)]
pub struct $name {
/// API name
pub name: &'static str,
pub version: &'static str,
pub create_fn: fn() -> $instance,
pub metadata_fn: fn() -> $metadata,
}
impl $name {
#[allow(dead_code)]
pub const fn new(
name: &'static str,
version: &'static str,
create_fn: fn() -> $instance,
metadata_fn: fn() -> $metadata,
) -> Self {
Self {
name,
version,
create_fn,
metadata_fn,
}
}
}
impl $crate::core::registration::Registration for $name {
type Instance = $instance;
type Metadata = $metadata;
fn name(&self) -> &str {
self.name
}
fn version(&self) -> &str {
self.version
}
fn create(&self) -> Self::Instance {
(self.create_fn)()
}
fn metadata(&self) -> Self::Metadata {
(self.metadata_fn)()
}
}
inventory::collect!($name);
};
}
#[cfg(test)]
mod tests {
use super::*;
pub struct MockInstance {
pub _data: String,
}
pub struct MockMetadata {
pub _name: String,
}
#[derive(Debug, Clone)]
pub struct InstanceTypeA {
pub value: i32,
}
#[derive(Debug, Clone)]
pub struct InstanceTypeB {
pub text: String,
}
#[derive(Debug, Clone)]
pub struct MetadataTypeA {
pub id: u64,
}
#[derive(Debug, Clone)]
pub struct MetadataTypeB {
pub tags: Vec<String>,
}
define_registration!(TestRegistration, MockInstance, MockMetadata);
#[test]
fn test_registration_trait_methods() {
let reg = TestRegistration::new(
"test_api",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
assert_eq!(reg.name(), "test_api");
assert_eq!(reg.version(), "v1");
let instance = reg.create();
assert!(instance._data == "test");
let metadata = reg.metadata();
assert!(metadata._name == "test");
}
#[test]
fn test_registration_trait_bounds() {
fn requires_send_sync<T: Send + Sync>() {}
fn requires_static<T: 'static>() {}
requires_send_sync::<TestRegistration>();
requires_static::<TestRegistration>();
}
#[test]
fn test_multiple_registrations() {
define_registration!(TestReg1, MockInstance, MockMetadata);
define_registration!(TestReg2, MockInstance, MockMetadata);
let reg1 = TestReg1::new(
"api1",
"v1",
|| MockInstance {
_data: "1".to_string(),
},
|| MockMetadata {
_name: "1".to_string(),
},
);
let reg2 = TestReg2::new(
"api2",
"v2",
|| MockInstance {
_data: "2".to_string(),
},
|| MockMetadata {
_name: "2".to_string(),
},
);
assert_eq!(reg1.name(), "api1");
assert_eq!(reg2.name(), "api2");
assert_ne!(reg1.name(), reg2.name());
let i1 = reg1.create();
assert_eq!(i1._data, "1");
let m1 = reg1.metadata();
assert_eq!(m1._name, "1");
let i2 = reg2.create();
assert_eq!(i2._data, "2");
let m2 = reg2.metadata();
assert_eq!(m2._name, "2");
}
#[test]
fn test_define_registration_macro_generates_struct() {
let reg = TestRegistration::new(
"test_api",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
assert_eq!(reg.name, "test_api");
assert_eq!(reg.version, "v1");
assert!(std::mem::size_of_val(®.create_fn) > 0);
assert!(std::mem::size_of_val(®.metadata_fn) > 0);
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_define_registration_macro_new_const_fn() {
const REG: TestRegistration = TestRegistration::new(
"const_api",
"v2",
|| MockInstance {
_data: "const".to_string(),
},
|| MockMetadata {
_name: "const".to_string(),
},
);
assert_eq!(REG.name, "const_api");
assert_eq!(REG.version, "v2");
let inst = REG.create();
assert_eq!(inst._data, "const");
let meta = REG.metadata();
assert_eq!(meta._name, "const");
}
#[test]
fn test_registration_trait_name_method() {
let reg = TestRegistration::new(
"api_name_test",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let name = reg.name();
assert_eq!(name, "api_name_test");
assert!(!name.is_empty());
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_trait_version_method() {
let reg = TestRegistration::new(
"test_api",
"v2.0.0",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let version = reg.version();
assert_eq!(version, "v2.0.0");
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_trait_create_method() {
let reg = TestRegistration::new(
"test_api",
"v1",
|| MockInstance {
_data: "created_instance".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let instance = reg.create();
assert_eq!(instance._data, "created_instance");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_trait_metadata_method() {
let reg = TestRegistration::new(
"test_api",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "metadata_value".to_string(),
},
);
let metadata = reg.metadata();
assert_eq!(metadata._name, "metadata_value");
let inst = reg.create();
assert_eq!(inst._data, "test");
}
#[test]
fn test_registration_send_sync_static_bounds() {
fn requires_send_sync<T: Send + Sync>() {}
fn requires_static<T: 'static>() {}
requires_send_sync::<TestRegistration>();
requires_static::<TestRegistration>();
fn requires_registration<T: Registration>() {}
requires_registration::<TestRegistration>();
}
#[test]
fn test_registration_clone_copy_traits() {
let reg = TestRegistration::new(
"test_api",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let reg_copy = reg;
assert_eq!(reg_copy.name(), "test_api");
let reg_cloned = reg;
assert_eq!(reg_cloned.name(), "test_api");
assert_eq!(reg_cloned.version(), "v1");
assert_eq!(reg.name(), "test_api");
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_debug_trait() {
let reg = TestRegistration::new(
"debug_api",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let debug_output = format!("{:?}", reg);
assert!(debug_output.contains("debug_api"));
assert!(debug_output.contains("v1"));
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_empty_name_and_version() {
let reg = TestRegistration::new(
"",
"",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
assert_eq!(reg.name(), "");
assert_eq!(reg.version(), "");
assert_eq!(reg.name, "");
assert_eq!(reg.version, "");
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_special_characters_name() {
let reg = TestRegistration::new(
"api-with-dashes_and_underscores",
"v1.0.0-alpha+build.123",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
assert_eq!(reg.name(), "api-with-dashes_and_underscores");
assert_eq!(reg.version(), "v1.0.0-alpha+build.123");
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_multiple_define_registration_same_scope() {
define_registration!(RegA, MockInstance, MockMetadata);
define_registration!(RegB, MockInstance, MockMetadata);
define_registration!(RegC, MockInstance, MockMetadata);
let a = RegA::new(
"a",
"v1",
|| MockInstance { _data: "a".into() },
|| MockMetadata { _name: "a".into() },
);
let b = RegB::new(
"b",
"v1",
|| MockInstance { _data: "b".into() },
|| MockMetadata { _name: "b".into() },
);
let c = RegC::new(
"c",
"v1",
|| MockInstance { _data: "c".into() },
|| MockMetadata { _name: "c".into() },
);
assert_eq!(a.name(), "a");
assert_eq!(b.name(), "b");
assert_eq!(c.name(), "c");
let ia = a.create();
assert_eq!(ia._data, "a");
let ma = a.metadata();
assert_eq!(ma._name, "a");
let ib = b.create();
assert_eq!(ib._data, "b");
let mb = b.metadata();
assert_eq!(mb._name, "b");
let ic = c.create();
assert_eq!(ic._data, "c");
let mc = c.metadata();
assert_eq!(mc._name, "c");
}
#[test]
fn test_registration_inventory_collect_macro() {
use std::any::TypeId;
let reg = TestRegistration::new(
"inventory_test",
"v1",
|| MockInstance {
_data: "test".to_string(),
},
|| MockMetadata {
_name: "test".to_string(),
},
);
let _type_id = TypeId::of::<TestRegistration>();
let _registration: &dyn Registration<Instance = MockInstance, Metadata = MockMetadata> =
®
let inst = reg.create();
assert_eq!(inst._data, "test");
let meta = reg.metadata();
assert_eq!(meta._name, "test");
}
#[test]
fn test_registration_different_instance_types() {
define_registration!(RegWithInstanceA, InstanceTypeA, MockMetadata);
define_registration!(RegWithInstanceB, InstanceTypeB, MockMetadata);
let reg_a = RegWithInstanceA::new(
"instance_a",
"v1",
|| InstanceTypeA { value: 42 },
|| MockMetadata {
_name: "a".to_string(),
},
);
let reg_b = RegWithInstanceB::new(
"instance_b",
"v1",
|| InstanceTypeB {
text: "hello".to_string(),
},
|| MockMetadata {
_name: "b".to_string(),
},
);
let instance_a = reg_a.create();
let instance_b = reg_b.create();
assert_eq!(instance_a.value, 42);
assert_eq!(instance_b.text, "hello");
let meta_a = reg_a.metadata();
assert_eq!(meta_a._name, "a");
let meta_b = reg_b.metadata();
assert_eq!(meta_b._name, "b");
}
#[test]
fn test_registration_different_metadata_types() {
define_registration!(RegWithMetadataA, MockInstance, MetadataTypeA);
define_registration!(RegWithMetadataB, MockInstance, MetadataTypeB);
let reg_a = RegWithMetadataA::new(
"metadata_a",
"v1",
|| MockInstance {
_data: "a".to_string(),
},
|| MetadataTypeA { id: 100 },
);
let reg_b = RegWithMetadataB::new(
"metadata_b",
"v1",
|| MockInstance {
_data: "b".to_string(),
},
|| MetadataTypeB {
tags: vec!["tag1".to_string(), "tag2".to_string()],
},
);
let metadata_a = reg_a.metadata();
let metadata_b = reg_b.metadata();
assert_eq!(metadata_a.id, 100);
assert_eq!(metadata_b.tags.len(), 2);
let inst_a = reg_a.create();
assert_eq!(inst_a._data, "a");
let inst_b = reg_b.create();
assert_eq!(inst_b._data, "b");
}
}