use crate::Shared;
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Instance<T: ?Sized + 'static> {
value: Shared<T>,
}
impl<T: ?Sized + 'static> Instance<T> {
pub fn new(value: Shared<T>) -> Self {
Self { value }
}
pub fn get(&self) -> &T {
&self.value
}
pub fn value(&self) -> Shared<T> {
self.value.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
struct TestData {
id: u32,
name: String,
}
#[derive(Debug, PartialEq)]
struct Counter {
value: u32,
}
trait Service: std::fmt::Debug {
fn name(&self) -> &str;
fn execute(&self) -> String;
}
#[derive(Debug)]
struct DatabaseService {
connection_string: String,
}
impl Service for DatabaseService {
fn name(&self) -> &str {
"DatabaseService"
}
fn execute(&self) -> String {
format!("Connected to: {}", self.connection_string)
}
}
#[derive(Debug)]
struct CacheService {
max_size: usize,
}
impl Service for CacheService {
fn name(&self) -> &str {
"CacheService"
}
fn execute(&self) -> String {
format!("Cache with max size: {}", self.max_size)
}
}
#[test]
fn test_instance_new_creates_instance() {
let data = Shared::new(TestData {
id: 1,
name: "test".to_string(),
});
let instance = Instance::new(data);
assert_eq!(instance.get().id, 1);
assert_eq!(instance.get().name, "test");
}
#[test]
fn test_instance_new_with_primitive() {
let value = Shared::new(42);
let instance = Instance::new(value);
assert_eq!(*instance.get(), 42);
}
#[test]
fn test_instance_new_with_string() {
let text = Shared::new(String::from("Hello, world!"));
let instance = Instance::new(text);
assert_eq!(instance.get().as_str(), "Hello, world!");
}
#[test]
fn test_instance_new_with_vec() {
let numbers = Shared::new(vec![1, 2, 3, 4, 5]);
let instance = Instance::new(numbers);
assert_eq!(instance.get().len(), 5);
assert_eq!(instance.get()[2], 3);
}
#[test]
fn test_instance_new_with_complex_struct() {
#[derive(Debug, PartialEq)]
struct ComplexData {
values: Vec<i32>,
metadata: std::collections::HashMap<String, String>,
}
let mut map = std::collections::HashMap::new();
map.insert("key1".to_string(), "value1".to_string());
let complex = Shared::new(ComplexData {
values: vec![10, 20, 30],
metadata: map,
});
let instance = Instance::new(complex);
assert_eq!(instance.get().values.len(), 3);
assert_eq!(instance.get().metadata.get("key1").unwrap(), "value1");
}
#[test]
fn test_get_returns_reference() {
let data = Shared::new(TestData {
id: 42,
name: "reference-test".to_string(),
});
let instance = Instance::new(data);
let reference = instance.get();
assert_eq!(reference.id, 42);
assert_eq!(reference.name, "reference-test");
}
#[test]
fn test_get_multiple_times_returns_same_data() {
let data = Shared::new(Counter { value: 100 });
let instance = Instance::new(data);
let ref1 = instance.get();
let ref2 = instance.get();
assert_eq!(ref1.value, ref2.value);
assert_eq!(ref1.value, 100);
}
#[test]
fn test_get_allows_field_access() {
let data = Shared::new(TestData {
id: 5,
name: "field-test".to_string(),
});
let instance = Instance::new(data);
assert_eq!(instance.get().id, 5);
assert_eq!(instance.get().name, "field-test");
}
#[test]
fn test_get_allows_method_calls() {
#[derive(Debug)]
struct Calculator {
base: i32,
}
impl Calculator {
fn add(&self, x: i32) -> i32 {
self.base + x
}
fn multiply(&self, x: i32) -> i32 {
self.base * x
}
}
let calc = Shared::new(Calculator { base: 10 });
let instance = Instance::new(calc);
assert_eq!(instance.get().add(5), 15);
assert_eq!(instance.get().multiply(3), 30);
}
#[test]
fn test_get_with_nested_access() {
#[derive(Debug)]
struct Inner {
value: String,
}
#[derive(Debug)]
struct Outer {
inner: Inner,
count: usize,
}
let outer = Shared::new(Outer {
inner: Inner {
value: "nested".to_string(),
},
count: 5,
});
let instance = Instance::new(outer);
assert_eq!(instance.get().inner.value, "nested");
assert_eq!(instance.get().count, 5);
}
#[test]
fn test_value_returns_cloned_shared() {
let data = Shared::new(TestData {
id: 99,
name: "clone-test".to_string(),
});
let instance = Instance::new(data);
let shared1 = instance.value();
let shared2 = instance.value();
assert!(Shared::ptr_eq(&shared1, &shared2));
}
#[test]
fn test_value_increments_reference_count() {
let data = Shared::new(TestData {
id: 1,
name: "refcount".to_string(),
});
let instance = Instance::new(data.clone());
#[cfg(feature = "thread-safe")]
let initial_count = std::sync::Arc::strong_count(&data);
#[cfg(not(feature = "thread-safe"))]
let initial_count = std::rc::Rc::strong_count(&data);
let _shared1 = instance.value();
#[cfg(feature = "thread-safe")]
let after_one = std::sync::Arc::strong_count(&data);
#[cfg(not(feature = "thread-safe"))]
let after_one = std::rc::Rc::strong_count(&data);
assert_eq!(after_one, initial_count + 1);
let _shared2 = instance.value();
#[cfg(feature = "thread-safe")]
let after_two = std::sync::Arc::strong_count(&data);
#[cfg(not(feature = "thread-safe"))]
let after_two = std::rc::Rc::strong_count(&data);
assert_eq!(after_two, initial_count + 2);
}
#[test]
fn test_value_can_be_stored() {
let data = Shared::new(TestData {
id: 10,
name: "storage-test".to_string(),
});
let instance = Instance::new(data);
let storage: Vec<Shared<TestData>> =
vec![instance.value(), instance.value(), instance.value()];
assert!(Shared::ptr_eq(&storage[0], &storage[1]));
assert!(Shared::ptr_eq(&storage[1], &storage[2]));
assert_eq!(storage[0].id, 10);
assert_eq!(storage[2].name, "storage-test");
}
#[test]
fn test_value_enables_sharing_across_components() {
struct ServiceA {
data: Shared<TestData>,
}
struct ServiceB {
data: Shared<TestData>,
}
let data = Shared::new(TestData {
id: 50,
name: "shared".to_string(),
});
let instance = Instance::new(data);
let service_a = ServiceA {
data: instance.value(),
};
let service_b = ServiceB {
data: instance.value(),
};
assert!(Shared::ptr_eq(&service_a.data, &service_b.data));
assert_eq!(service_a.data.id, 50);
assert_eq!(service_b.data.id, 50);
}
#[test]
fn test_instance_with_trait_object() {
let service: Shared<dyn Service> = Shared::new(DatabaseService {
connection_string: "postgresql://localhost".to_string(),
});
let instance = Instance::<dyn Service>::new(service);
assert_eq!(instance.get().name(), "DatabaseService");
assert!(instance.get().execute().contains("postgresql"));
}
#[test]
fn test_instance_trait_object_polymorphism() {
let db_service: Shared<dyn Service> = Shared::new(DatabaseService {
connection_string: "postgresql://localhost".to_string(),
});
let cache_service: Shared<dyn Service> = Shared::new(CacheService { max_size: 1000 });
let instance1 = Instance::<dyn Service>::new(db_service);
let instance2 = Instance::<dyn Service>::new(cache_service);
assert_eq!(instance1.get().name(), "DatabaseService");
assert_eq!(instance2.get().name(), "CacheService");
}
#[test]
fn test_trait_object_value_cloning() {
let service: Shared<dyn Service> = Shared::new(CacheService { max_size: 500 });
let instance = Instance::<dyn Service>::new(service);
let shared1 = instance.value();
let shared2 = instance.value();
assert!(Shared::ptr_eq(&shared1, &shared2));
assert_eq!(shared1.name(), "CacheService");
}
#[test]
fn test_instance_with_empty_struct() {
#[derive(Debug, PartialEq)]
struct Empty;
let empty = Shared::new(Empty);
let instance = Instance::new(empty);
assert_eq!(*instance.get(), Empty);
}
#[test]
fn test_instance_with_large_struct() {
#[derive(Debug)]
struct LargeStruct {
data: [u8; 1024],
}
let large = Shared::new(LargeStruct { data: [42; 1024] });
let instance = Instance::new(large);
assert_eq!(instance.get().data[0], 42);
assert_eq!(instance.get().data[1023], 42);
}
#[test]
fn test_multiple_instances_same_shared() {
let data = Shared::new(TestData {
id: 777,
name: "multi-instance".to_string(),
});
let instance1 = Instance::new(data.clone());
let instance2 = Instance::new(data.clone());
let instance3 = Instance::new(data.clone());
assert!(Shared::ptr_eq(&instance1.value(), &instance2.value()));
assert!(Shared::ptr_eq(&instance2.value(), &instance3.value()));
assert_eq!(instance1.get().id, 777);
assert_eq!(instance2.get().id, 777);
assert_eq!(instance3.get().id, 777);
}
#[test]
fn test_instance_outlives_original_shared() {
let instance = {
let data = Shared::new(TestData {
id: 888,
name: "lifetime-test".to_string(),
});
Instance::new(data)
};
assert_eq!(instance.get().id, 888);
assert_eq!(instance.get().name, "lifetime-test");
}
#[test]
fn test_nested_instances() {
#[derive(Debug)]
struct Inner {
value: u32,
}
#[derive(Debug)]
struct Outer {
inner_instance: Instance<Inner>,
}
let inner = Instance::new(Shared::new(Inner { value: 42 }));
let outer = Instance::new(Shared::new(Outer {
inner_instance: inner,
}));
assert_eq!(outer.get().inner_instance.get().value, 42);
}
#[cfg(feature = "debug")]
#[test]
fn test_instance_debug_format() {
let data = Shared::new(TestData {
id: 1,
name: "debug-test".to_string(),
});
let instance = Instance::new(data);
let debug_str = format!("{:?}", instance);
assert!(debug_str.contains("Instance"));
}
#[cfg(feature = "thread-safe")]
#[test]
fn test_instance_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Instance<TestData>>();
}
#[cfg(feature = "thread-safe")]
#[test]
fn test_instance_can_be_shared_across_threads() {
use std::sync::Arc;
use std::thread;
let instance = Arc::new(Instance::new(Shared::new(TestData {
id: 123,
name: "thread-test".to_string(),
})));
let handles: Vec<_> = (0..4)
.map(|_| {
let instance_clone = Arc::clone(&instance);
thread::spawn(move || instance_clone.get().id)
})
.collect();
for handle in handles {
let result = handle.join().unwrap();
assert_eq!(result, 123);
}
}
#[cfg(feature = "thread-safe")]
#[test]
fn test_instance_value_can_be_sent_to_thread() {
use std::thread;
let instance = Instance::new(Shared::new(TestData {
id: 456,
name: "send-test".to_string(),
}));
let shared = instance.value();
let handle = thread::spawn(move || shared.id);
let result = handle.join().unwrap();
assert_eq!(result, 456);
}
#[cfg(feature = "thread-safe")]
#[test]
fn test_multiple_threads_accessing_same_instance() {
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::thread;
#[derive(Debug)]
struct SharedCounter {
value: AtomicU32,
}
let instance = Arc::new(Instance::new(Shared::new(SharedCounter {
value: AtomicU32::new(0),
})));
let handles: Vec<_> = (0..10)
.map(|_| {
let instance_clone = Arc::clone(&instance);
thread::spawn(move || {
for _ in 0..100 {
instance_clone.get().value.fetch_add(1, Ordering::SeqCst);
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
let final_value = instance.get().value.load(Ordering::SeqCst);
assert_eq!(final_value, 1000); }
#[test]
fn test_realistic_dependency_injection_scenario() {
#[derive(Debug)]
struct Config {
database_url: String,
cache_ttl: u64,
}
#[derive(Debug)]
struct Application {
config: Shared<Config>,
}
impl Application {
fn new(config: Shared<Config>) -> Self {
Self { config }
}
fn get_database_url(&self) -> &str {
&self.config.database_url
}
}
let config_instance = Instance::new(Shared::new(Config {
database_url: "postgresql://prod".to_string(),
cache_ttl: 3600,
}));
let app = Application::new(config_instance.value());
assert_eq!(app.get_database_url(), "postgresql://prod");
assert_eq!(app.config.cache_ttl, 3600);
}
#[test]
fn test_instance_in_collection() {
let instances: Vec<Instance<TestData>> = vec![
Instance::new(Shared::new(TestData {
id: 1,
name: "one".to_string(),
})),
Instance::new(Shared::new(TestData {
id: 2,
name: "two".to_string(),
})),
Instance::new(Shared::new(TestData {
id: 3,
name: "three".to_string(),
})),
];
assert_eq!(instances.len(), 3);
assert_eq!(instances[0].get().id, 1);
assert_eq!(instances[1].get().name, "two");
assert_eq!(instances[2].get().id, 3);
}
#[test]
fn test_instance_with_option() {
let some_instance = Some(Instance::new(Shared::new(TestData {
id: 100,
name: "optional".to_string(),
})));
let none_instance: Option<Instance<TestData>> = None;
assert!(some_instance.is_some());
assert!(none_instance.is_none());
if let Some(instance) = some_instance {
assert_eq!(instance.get().id, 100);
}
}
}