1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
//! Manager for dependencies between objects for the Rust language. //! //! # How to use //! //! The main type of this crate is the [`ServiceContainer`]. There are multiple //! ways to initialize the container. See the documentation on ServiceContainer //! for more information. The easiest way is with `new()`. //! //! ```rust //! use rscontainer::ServiceContainer; //! let mut container = ServiceContainer::new(); //! ``` //! //! To configure the container, such as overriding the default constructors, //! use the [`ContainerBuilder`]. //! //! ```rust //! # use rscontainer::{IOwned, Resolver}; //! # struct MyService(u32); //! # impl IOwned for MyService { //! # type Instance = MyService; //! # type Parameters = u32; //! # type Error = (); //! # fn construct(_: Resolver, val: u32) -> Result<MyService, ()> { //! # Ok(MyService(val)) //! # } //! # } //! use rscontainer::ServiceContainer; //! let mut container = ServiceContainer::builder() //! .with_owned_constructor::<MyService>(|_resolver, value| { //! Ok(MyService(value)) //! }) //! .build(); //! ``` //! //! ## Resolving instances //! //! There are different kind of instances: //! //! * **Owned instances**: a fresh instance to be used in an owned scope. This //! instance will not be stored in the service container, you will get a new //! instance each time you resolve an owned instance. See //! [`Resolver::owned()`]. //! * **Shared instances**: an instance behind a smart pointer that is stored //! in the service container. You will get the same instance each time you //! resolve a shared service. See [`Resolver::shared()`] and [`Shared<T>`]. //! * **Some instances**: an enum over owned and shared instances. Use this in a //! type when you want the user of your type to decide what kind of instance //! they want to supply. See [`Instance`], [`Resolver::shared_instance()`] and //! [`Resolver::owned_instance()`]. //! //! To resolve instances, you first need to acquire a [`Resolver`]. //! //! ```rust //! # use rscontainer::ServiceContainer; //! # let mut container = ServiceContainer::new(); //! let mut resolver = container.resolver(); //! ``` //! //! To get an instance, you use one of the resolver methods. To resolve an //! **owned instance**, use the [`Resolver::owned()`] method. An owned service //! can define parameters that need to be supplied to the `owned()` method. //! //! ```rust //! # use rscontainer::{IOwned, Resolver, ServiceContainer}; //! # struct MyService(u32); //! # impl IOwned for MyService { //! # type Instance = MyService; //! # type Parameters = u32; //! # type Error = (); //! # fn construct(_: Resolver, val: u32) -> Result<MyService, ()> { //! # Ok(MyService(val)) //! # } //! # } //! # fn main() -> Result<(), ()> { //! # let mut container = ServiceContainer::new(); //! # let mut resolver = container.resolver(); //! let mut owned_service = resolver.owned::<MyService>(120)?; //! # Ok(()) } //! ``` //! //! To resolve a **shared instance**, use the [`Resolver::shared()`] method. //! The first time that this service is resolved, it will be contructed, stored //! in the container and the pointer is returned. Every other time the service //! is resolved that same pointer will be cloned and returned. Therefore it is //! not possible to supply parameters. //! //! ```rust //! # use rscontainer::{IShared, Resolver, ServiceContainer}; //! # use std::sync::{Arc, Mutex}; //! # struct MyService(u32); //! # impl IShared for MyService { //! # type Pointer = Arc<Mutex<MyService>>; //! # type Target = MyService; //! # type Error = (); //! # fn construct(_: Resolver) -> Result<Arc<Mutex<MyService>>, ()> { //! # Ok(Arc::new(Mutex::new(MyService(543)))) //! # } //! # } //! # fn main() -> Result<(), ()> { //! # let mut container = ServiceContainer::new(); //! # let mut resolver = container.resolver(); //! let shared_service = resolver.shared::<MyService>()?; //! # Ok(()) } //! ``` //! //! ## Working with instances //! //! An owned instance is just a normal, owned instance, therefore you can do //! with it whatever you want. But a shared instance is always behind a smart //! pointer and a locking or borrowing mechanism. To use the instance, you need //! to use one of the access methods: [`Shared::access()`], //! [`Shared::access_mut()`], [`Shared::try_access()`] and //! [`Shared::try_access_mut()`], which borrow or lock the instance for the //! lifetime of the supplied closure. These access methods take into account //! that the service may be poisoned. See [`Poisoning`] for more information. //! //! ```rust //! # use rscontainer::{IShared, Resolver, ServiceContainer}; //! # use std::sync::{Arc, Mutex}; //! # struct MyService(u32); //! # impl MyService { fn get_value(&self) -> u32 { self.0 } } //! # impl IShared for MyService { //! # type Pointer = Arc<Mutex<MyService>>; //! # type Target = MyService; //! # type Error = (); //! # fn construct(_: Resolver) -> Result<Arc<Mutex<MyService>>, ()> { //! # Ok(Arc::new(Mutex::new(MyService(543)))) //! # } //! # } //! # fn main() -> Result<(), ()> { //! # let mut container = ServiceContainer::new(); //! # let mut resolver = container.resolver(); //! # let shared_service = resolver.shared::<MyService>()?; //! let value = shared_service.access(|service| { //! let service = service.assert_healthy(); //! service.get_value() //! }); //! # Ok(()) } //! ``` //! //! ## Using a type as a service //! //! To be able to resolve a type through the service container, there needs to //! be an implementation of [`IShared`] and/or [`IOwned`] for it. These traits //! define a constructor method. For an owned service it will be called each //! time it is resolved. For a shared service it will only be called the first //! time. //! //! The constructors do not return `Self`, but rather an associated type //! defined on the traits. This makes it possible to resolve every type through //! the container without having to create a newtype wrapper. //! //! The constructors also receive a [`Resolver`], which can be used to //! recursively construct dependencies of the service. This is rscontainer's //! implementation of *dependency injection*. //! //! # Example //! //! ```rust //! use std::time::Instant; //! use std::rc::Rc; //! use std::cell::RefCell; //! use rscontainer::{IShared, Resolver, ServiceContainer}; //! //! struct InstantService; //! impl IShared for InstantService { //! type Pointer = Rc<RefCell<Instant>>; //! type Target = Instant; //! type Error = (); //! //! fn construct(_: Resolver) -> Result<Self::Pointer, Self::Error> { //! Ok(Rc::new(RefCell::new(Instant::now()))) //! } //! } //! //! fn main() { //! let mut container = ServiceContainer::new(); //! let instant = container.resolver().shared::<InstantService>().unwrap(); //! instant.access(|instant| { //! let instant = instant.assert_healthy(); //! println!("{:?}", instant); //! }); //! } //! ``` mod access; mod builder; mod container; mod getters; mod internal_helpers; mod pointers; mod resolver; mod service_traits; pub use self::access::{Access, Poisoning}; pub use self::builder::ContainerBuilder; pub use self::container::ServiceContainer; pub use self::getters::{Instance, Shared}; pub use self::resolver::Resolver; pub use self::service_traits::{IOwned, IShared}; /// Types for extending the functionality of rscontainer. pub mod internals { pub use crate::access::{IAccess, IAccessMut}; pub use crate::pointers::ISharedPointer; }