├── .gitignore ├── .travis.yml ├── Cargo.toml ├── src ├── lib.rs ├── collection.rs ├── scope.rs ├── constructed.rs ├── inceptor.rs └── deps.rs ├── LICENSE-MIT ├── README.md ├── examples └── bridge.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /target-install 3 | /Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | after_success: | 4 | cargo doc \ 5 | && echo '' > target/doc/index.html && \ 6 | sudo pip install ghp-import && \ 7 | ghp-import -n target/doc && \ 8 | git push -qf https://${TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "di" 4 | version = "0.2.0" 5 | authors = [ 6 | "Rust DI Developers", 7 | "Nerijus Arlauskas " 8 | ] 9 | 10 | documentation = "http://nercury.github.io/di-rs/di/index.html" 11 | repository = "https://github.com/Nercury/di-rs" 12 | 13 | readme = "README.md" 14 | 15 | keywords = [ 16 | "di", "dependency", "injection" 17 | ] 18 | 19 | license = "MIT OR Apache-2.0" 20 | 21 | description = "Ownership-driven dependency injection toolkit." 22 | 23 | [lib] 24 | name = "di" 25 | doctest = true 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Fork me on GitHub 3 | //! 4 | //! 5 | //! 6 | 7 | mod deps; 8 | mod collection; 9 | mod scope; 10 | mod inceptor; 11 | mod constructed; 12 | 13 | use std::result; 14 | use std::error; 15 | 16 | pub use constructed::MaybeMutexGuard; 17 | pub use collection::Collection; 18 | pub use scope::Scope; 19 | pub use deps::Deps; 20 | 21 | pub type Result = result::Result>; 22 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 The Rust DI Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust DI 2 | 3 | Ownership-driven Dependency Injection for Rust. 4 | 5 | [![Build Status](https://travis-ci.org/Nercury/di-rs.svg?branch=master)](https://travis-ci.org/Nercury/di-rs) 6 | 7 | Refactoring DI into more rusty ownership-driven architecture. 8 | 9 | ## Documentation 10 | 11 | - [Read overview and motivation for creating this library](http://nercury.github.io/di-rs) 12 | 13 | ## Usage 14 | 15 | __This library is being refactored.__ 16 | 17 | Put this in your `Cargo.toml`: 18 | 19 | ```toml 20 | [dependencies] 21 | di = "*" 22 | ``` 23 | 24 | And this in your crate root: 25 | 26 | ```rust 27 | extern crate di; 28 | ``` 29 | 30 | ## License 31 | 32 | Licensed under either of 33 | 34 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 35 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 36 | 37 | at your option. 38 | 39 | ### Contribution 40 | 41 | Unless you explicitly state otherwise, any contribution intentionally 42 | submitted for inclusion in the work by you, as defined in the Apache-2.0 43 | license, shall be dual licensed as above, without any additional terms or 44 | conditions. 45 | -------------------------------------------------------------------------------- /examples/bridge.rs: -------------------------------------------------------------------------------- 1 | extern crate di; 2 | 3 | use di::Deps; 4 | use std::sync::Arc; 5 | 6 | struct Window { 7 | pub resize_listeners: Vec>, 8 | } 9 | 10 | struct Logger { 11 | pub log_fn: Arc, 12 | } 13 | 14 | impl Window { 15 | fn new() -> Window { 16 | Window { resize_listeners: Vec::new() } 17 | } 18 | 19 | fn resize(&self, w: i32, h: i32) { 20 | for listener in &self.resize_listeners { 21 | listener(w, h); 22 | } 23 | } 24 | } 25 | 26 | impl Logger { 27 | fn new(log_function: F) -> Logger 28 | where F: Fn(&str) + Send + Sync + 'static 29 | { 30 | Logger { log_fn: Arc::new(log_function) } 31 | } 32 | } 33 | 34 | fn main() { 35 | let mut deps = Deps::new(); 36 | 37 | deps.bridge(|window: &mut Window, logger: &mut Logger| { 38 | // while window and logger both exist, log messages to logger 39 | let log_fn_clone = logger.log_fn.clone(); 40 | window.resize_listeners.push(Box::new(move |w, h| { 41 | log_fn_clone(&format!("window resized to w: {} and h: {}", w, h)); 42 | })); 43 | Ok(()) 44 | }); 45 | 46 | let mut window = deps.create(Window::new()).unwrap(); 47 | deps.create(Logger::new(|message| println!("message: {:?}", message))).unwrap(); 48 | 49 | window.lock().unwrap().resize(12, 13); 50 | } 51 | -------------------------------------------------------------------------------- /src/collection.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::convert; 3 | use std::slice; 4 | use std::vec; 5 | 6 | pub struct Collection { 7 | items: Vec, 8 | } 9 | 10 | impl fmt::Debug for Collection 11 | where T: fmt::Debug 12 | { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | f.debug_list().entries(self.items.iter()).finish() 15 | } 16 | } 17 | 18 | impl Collection { 19 | pub fn new() -> Collection { 20 | Collection { items: Vec::new() } 21 | } 22 | 23 | pub fn push(&mut self, item: T) { 24 | self.items.push(item) 25 | } 26 | 27 | pub fn iter<'a>(&'a self) -> slice::Iter<'a, T> { 28 | self.into_iter() 29 | } 30 | } 31 | 32 | impl convert::AsRef<[T]> for Collection { 33 | fn as_ref(&self) -> &[T] { 34 | &self.items 35 | } 36 | } 37 | 38 | impl<'a, T> IntoIterator for &'a Collection { 39 | type IntoIter = slice::Iter<'a, T>; 40 | type Item = &'a T; 41 | 42 | fn into_iter(self) -> slice::Iter<'a, T> { 43 | self.items.iter() 44 | } 45 | } 46 | 47 | impl IntoIterator for Collection { 48 | type IntoIter = vec::IntoIter; 49 | type Item = T; 50 | 51 | fn into_iter(self) -> vec::IntoIter { 52 | self.items.into_iter() 53 | } 54 | } 55 | 56 | impl Into> for Collection { 57 | fn into(self) -> Vec { 58 | self.items 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::sync::Arc; 3 | use std::sync::LockResult; 4 | use std::mem; 5 | use constructed::{Instance, AnyInstance, MaybeMutexGuard}; 6 | 7 | #[derive(Debug)] 8 | pub struct Scope { 9 | obj: Instance, 10 | childs: Vec>, 11 | } 12 | 13 | impl Scope { 14 | pub fn from_any_instance(obj: AnyInstance, childs: Vec>) -> Scope { 15 | Scope { 16 | obj: obj.downcast(), 17 | childs: childs, 18 | } 19 | } 20 | 21 | pub fn explode(self) -> T { 22 | mem::drop(self.childs); // Childs contain a special "destructor" that 23 | // will free up the arc when dropped. 24 | // To make behaviour consistent, we are dropping childs before 25 | // parent in all cases. 26 | match self.obj { 27 | Instance::Isolated(obj) => obj, 28 | Instance::Shared(arc) => { 29 | Arc::try_unwrap(arc) 30 | .ok() 31 | .expect("expected arc to be last remaining") 32 | .into_inner() 33 | .expect("expected to lock value before exploding") 34 | } 35 | } 36 | } 37 | 38 | pub fn lock<'a>(&'a mut self) -> LockResult> { 39 | self.obj.lock() 40 | } 41 | 42 | pub fn get_instance(&self) -> &Instance { 43 | &self.obj 44 | } 45 | } 46 | 47 | unsafe impl Send for Scope {} 48 | unsafe impl Sync for Scope {} 49 | -------------------------------------------------------------------------------- /src/constructed.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::ops::{Deref, DerefMut}; 3 | use std::sync::{Mutex, Arc, LockResult, PoisonError, MutexGuard}; 4 | 5 | enum MaybeMutexGuardValue<'a, T: 'a> { 6 | Guard(MutexGuard<'a, T>), 7 | Ref(&'a mut T), 8 | } 9 | 10 | pub struct MaybeMutexGuard<'a, T: 'a> { 11 | inner: MaybeMutexGuardValue<'a, T>, 12 | } 13 | 14 | #[derive(Debug)] 15 | pub enum Instance { 16 | Isolated(T), 17 | Shared(Arc>), 18 | } 19 | 20 | impl Instance { 21 | pub fn lock<'a>(&'a mut self) -> LockResult> { 22 | match *self { 23 | Instance::Isolated(ref mut val) => { 24 | Ok(MaybeMutexGuard { inner: MaybeMutexGuardValue::Ref(val) }) 25 | } 26 | Instance::Shared(ref mut val) => { 27 | match val.lock() { 28 | Ok(guard) => Ok(MaybeMutexGuard { inner: MaybeMutexGuardValue::Guard(guard) }), 29 | Err(e) => { 30 | Err(PoisonError::new(MaybeMutexGuard { 31 | inner: MaybeMutexGuardValue::Guard(e.into_inner()), 32 | })) 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | impl<'a, T> Deref for MaybeMutexGuard<'a, T> { 41 | type Target = T; 42 | 43 | fn deref(&self) -> &T { 44 | match self.inner { 45 | MaybeMutexGuardValue::Guard(ref val) => val, 46 | MaybeMutexGuardValue::Ref(ref val) => val, 47 | } 48 | } 49 | } 50 | 51 | impl<'a, T> DerefMut for MaybeMutexGuard<'a, T> { 52 | fn deref_mut(&mut self) -> &mut T { 53 | match self.inner { 54 | MaybeMutexGuardValue::Guard(ref mut val) => val, 55 | MaybeMutexGuardValue::Ref(ref mut val) => val, 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | pub enum AnyInstance { 62 | Isolated(Box), 63 | Shared(Box), 64 | } 65 | 66 | impl AnyInstance { 67 | pub fn downcast(self) -> Instance { 68 | match self { 69 | AnyInstance::Isolated(parent) => { 70 | Instance::Isolated(*parent.downcast() 71 | .expect("expected AnyInstance::Isolated to downcast into type")) 72 | } 73 | AnyInstance::Shared(parent) => { 74 | Instance::Shared(*parent.downcast() 75 | .expect("expected AnyInstance::Shared to downcast into type")) 76 | } 77 | } 78 | } 79 | } 80 | 81 | pub struct Constructed { 82 | pub children: Vec>, 83 | } 84 | 85 | pub struct ConstructedShared { 86 | pub children: Vec>, 87 | } 88 | -------------------------------------------------------------------------------- /src/inceptor.rs: -------------------------------------------------------------------------------- 1 | //! It incepts. 2 | 3 | use std::sync::{Arc, Mutex}; 4 | use std::any::Any; 5 | use std::mem; 6 | use Result; 7 | 8 | pub struct Inceptor { 9 | d1: Vec>>>, 10 | d2: Vec>>>, 11 | used_size1: usize, 12 | used_size2: usize, 13 | constructor: Arc Result>> + Send + Sync>, 14 | } 15 | 16 | impl Inceptor { 17 | pub fn new(constructor: F) -> Inceptor 18 | where F: Fn(&mut T1, &mut T2) -> Result>> + Send + Sync 19 | { 20 | Inceptor { 21 | d1: Vec::new(), 22 | d2: Vec::new(), 23 | used_size1: 0, 24 | used_size2: 0, 25 | constructor: Arc::new(constructor), 26 | } 27 | } 28 | 29 | pub fn new_with_ignored_return_val(constructor: F) -> Inceptor 30 | where C: 'static + Any, 31 | F: for<'r> Fn(&mut T1, &mut T2) -> Result + 'static + Send + Sync 32 | { 33 | Self::new(move |p1: &mut T1, p2: &mut T2| -> Result>> { 34 | try!(constructor(p1, p2)); 35 | Ok(None) 36 | }) 37 | } 38 | 39 | pub fn new_with_return_val(constructor: F) -> Inceptor 40 | where C: 'static + Any, 41 | F: for<'r> Fn(&mut T1, &mut T2) -> Result + 'static + Send + Sync 42 | { 43 | Self::new(move |p1: &mut T1, p2: &mut T2| -> Result>> { 44 | Ok(Some(Box::new(try!(constructor(p1, p2))))) 45 | }) 46 | } 47 | 48 | fn invoke(&mut self, i1: usize, i2: usize) -> Result>> { 49 | let val1: &mut Arc> = match *self.d1 50 | .get_mut(i1) 51 | .expect("expected to find i1") { 52 | Some(ref mut val) => val, 53 | None => unreachable!("expected i1 to exist at slot"), 54 | }; 55 | let val2 = match *self.d2 56 | .get_mut(i2) 57 | .expect("expected to find i2") { 58 | Some(ref mut val) => val, 59 | None => unreachable!("expected i2 to exist at slot"), 60 | }; 61 | let mut locked1 = val1.lock().expect("expected to lock val1"); 62 | let mut locked2 = val2.lock().expect("expected to lock val2"); 63 | (self.constructor)(&mut locked1, &mut locked2) 64 | } 65 | 66 | pub fn incept_1(&mut self, parent: Arc>) -> Result<(usize, Vec>)> { 67 | let id = insert_into_vec(&mut self.d1, parent, &mut self.used_size1); 68 | let mut results = Vec::new(); 69 | for i2 in 0..self.d2.len() { 70 | if !self.d2[i2].is_none() { 71 | match try!(self.invoke(id, i2)) { 72 | Some(res) => results.push(res), 73 | None => (), 74 | } 75 | } 76 | } 77 | Ok((id, results)) 78 | } 79 | 80 | pub fn incept_2(&mut self, parent: Arc>) -> Result<(usize, Vec>)> { 81 | let id = insert_into_vec(&mut self.d2, parent, &mut self.used_size2); 82 | let mut results = Vec::new(); 83 | for i1 in 0..self.d1.len() { 84 | if !self.d1[i1].is_none() { 85 | match try!(self.invoke(i1, id)) { 86 | Some(res) => results.push(res), 87 | None => (), 88 | } 89 | } 90 | } 91 | Ok((id, results)) 92 | } 93 | 94 | pub fn destroy_1(&mut self, id: usize) { 95 | let mut tmp: Option>> = None; 96 | mem::swap(&mut tmp, 97 | self.d1 98 | .get_mut(id) 99 | .expect(&format!("expected to find destroy_1 value {:?}", id))); 100 | 101 | truncate_to_used_elements_if_removed_id_is_last(&mut self.d1, id, &mut self.used_size1); 102 | } 103 | 104 | pub fn destroy_2(&mut self, id: usize) { 105 | let mut tmp: Option>> = None; 106 | mem::swap(&mut tmp, 107 | self.d2 108 | .get_mut(id) 109 | .expect(&format!("expected to find destroy_2 value {:?}", id))); 110 | 111 | truncate_to_used_elements_if_removed_id_is_last(&mut self.d2, id, &mut self.used_size2); 112 | } 113 | } 114 | 115 | fn insert_into_vec(data: &mut Vec>>>, 116 | value: Arc>, 117 | used_size: &mut usize) 118 | -> usize { 119 | for (i, item) in data.iter_mut().enumerate() { 120 | if item.is_none() { 121 | let mut tmp = Some(value); 122 | mem::swap(&mut tmp, item); 123 | if i + 1 > *used_size { 124 | *used_size = i + 1; 125 | } 126 | return i; 127 | } 128 | } 129 | let index = data.len(); 130 | data.push(Some(value)); 131 | if data.len() > *used_size { 132 | *used_size = data.len(); 133 | } 134 | index 135 | } 136 | 137 | fn truncate_to_used_elements_if_removed_id_is_last(data: &mut Vec>>>, 138 | removed_id: usize, 139 | used_size: &mut usize) { 140 | if removed_id + 1 != *used_size { 141 | return; 142 | } 143 | while *used_size > 0 { 144 | *used_size -= 1; 145 | if *used_size == 0 || !data[*used_size - 1].is_none() { 146 | break; 147 | } 148 | } 149 | data.truncate(*used_size); 150 | } 151 | 152 | pub struct Destructor { 153 | /// Inceptor to clean 154 | inceptor: Arc>>, 155 | /// Which method, 1 or 2 156 | index: usize, 157 | /// Id to clean 158 | id: usize, 159 | } 160 | 161 | impl Destructor { 162 | pub fn new(inceptor: Arc>>, 163 | index: usize, 164 | id: usize) 165 | -> Destructor { 166 | Destructor { 167 | inceptor: inceptor, 168 | index: index, 169 | id: id, 170 | } 171 | } 172 | } 173 | 174 | impl Drop for Destructor { 175 | fn drop(&mut self) { 176 | if self.index == 1 { 177 | self.inceptor.lock().expect("failed to lock").destroy_1(self.id); 178 | } else if self.index == 2 { 179 | self.inceptor.lock().expect("failed to lock").destroy_2(self.id); 180 | } 181 | } 182 | } 183 | 184 | #[cfg(test)] 185 | mod test { 186 | use std::any::Any; 187 | use std::sync::{Arc, Mutex}; 188 | use super::*; 189 | 190 | fn count_not_none(data: &mut Vec>>>) -> usize { 191 | data.iter().filter(|v| !v.is_none()).count() 192 | } 193 | 194 | fn ic_with_val() -> Inceptor { 195 | Inceptor::::new(move |_a, _b| Ok(Some(Box::new(42)))) 196 | } 197 | 198 | #[test] 199 | fn should_register_and_destroy_first_arg() { 200 | let mut ic = ic_with_val::(); 201 | let (id, instances) = ic.incept_1(Arc::new(Mutex::new(11))) 202 | .expect("failed to incept first arg"); 203 | 204 | assert_eq!(instances.len(), 0); 205 | assert_eq!(count_not_none(&mut ic.d1), 1); 206 | 207 | ic.destroy_1(id); 208 | 209 | assert_eq!(count_not_none(&mut ic.d1), 0); 210 | } 211 | 212 | #[test] 213 | fn should_register_and_destroy_second_arg() { 214 | let mut ic = ic_with_val::(); 215 | let (id, instances) = ic.incept_2(Arc::new(Mutex::new(false))) 216 | .expect("failed to incept first arg"); 217 | 218 | assert_eq!(instances.len(), 0); 219 | assert_eq!(count_not_none(&mut ic.d2), 1); 220 | 221 | ic.destroy_2(id); 222 | 223 | assert_eq!(count_not_none(&mut ic.d2), 0); 224 | } 225 | 226 | #[test] 227 | fn should_create_and_destroy_instances_for_all_existing_items() { 228 | let mut ic = ic_with_val::(); 229 | let mut value_num_3 = Arc::new(Mutex::new(3)); 230 | let (_, _) = ic.incept_1(Arc::new(Mutex::new(1))).unwrap(); 231 | let (_, _) = ic.incept_1(Arc::new(Mutex::new(2))).unwrap(); 232 | let (id3, _) = ic.incept_1(value_num_3.clone()).unwrap(); 233 | assert_eq!(count_not_none(&mut ic.d1), 3); 234 | 235 | let (other_id, instances) = ic.incept_2(Arc::new(Mutex::new(false))).unwrap(); 236 | assert_eq!(instances.len(), 3); 237 | assert_eq!(count_not_none(&mut ic.d2), 1); 238 | 239 | // should not be possible to take out value from arc 240 | value_num_3 = Arc::try_unwrap(value_num_3).unwrap_err(); 241 | // destroying parent should free up the instance that was created using it 242 | ic.destroy_1(id3); 243 | assert_eq!(count_not_none(&mut ic.d1), 2); 244 | // should be possible to take out value from arc 245 | { 246 | let val = Arc::try_unwrap(value_num_3).expect("expected arc refcount 1"); 247 | assert_eq!(3, *val.lock().unwrap()); 248 | } 249 | // memory should be freed 250 | assert_eq!(ic.d1.len(), 2); 251 | 252 | ic.destroy_2(other_id); 253 | assert_eq!(count_not_none(&mut ic.d2), 0); 254 | 255 | // memory should be freed 256 | assert_eq!(ic.d2.len(), 0); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/deps.rs: -------------------------------------------------------------------------------- 1 | use std::any::{Any, TypeId}; 2 | use std::sync::{Arc, Mutex}; 3 | use std::collections::HashMap; 4 | use std::collections::hash_map::Entry; 5 | use constructed::{Constructed, ConstructedShared, AnyInstance}; 6 | use inceptor::{Inceptor, Destructor}; 7 | use {Result, Collection, Scope}; 8 | 9 | pub struct Deps { 10 | /// Ignored type (). 11 | empty_type: TypeId, 12 | /// List of functions that constructs all childs for a type 13 | /// and returns value wrapped in Any that must live as long as the parent type. 14 | isolated_constructors: HashMap) -> Result + Send + Sync>>>, 16 | /// List of functions that constructs all childs for a type wrapped in Box>> as Box 17 | /// and returns value wrapped in Any that must live as long as the parent type. 18 | shared_constructors: HashMap) -> Result + Send + Sync>>>, 19 | /// List of callbacks to invoke after a value and all its dependencies were created. 20 | type_scope_created: HashMap Result<()> + Send + Sync>>>, 22 | /// List of inceptors that manage shared dependency bridge creation for type pairs. 23 | inceptors: HashMap<(TypeId, TypeId), Box>, 24 | } 25 | 26 | fn to_shared(not_shared: Box) -> Box { 27 | let parent: T = *not_shared.downcast::() 28 | .expect("expected downcast to P when \ 29 | changing to shared P"); 30 | Box::new(Arc::new(Mutex::new(parent))) 31 | } 32 | 33 | impl Deps { 34 | pub fn new() -> Deps { 35 | Deps { 36 | empty_type: TypeId::of::<()>(), 37 | isolated_constructors: HashMap::new(), 38 | shared_constructors: HashMap::new(), 39 | type_scope_created: HashMap::new(), 40 | inceptors: HashMap::new(), 41 | } 42 | } 43 | 44 | /// Create dependencies for specified `obj` and return a wrapper `Scope` object. 45 | /// 46 | /// The wrapper `Scope` keeps ownership of all children together with parent object. 47 | pub fn create(&self, obj: P) -> Result> { 48 | let (parent, deps) = 49 | try!(self.create_deps_for_any_parent(TypeId::of::

(), Box::new(obj), to_shared::

)); 50 | Ok(Scope::from_any_instance(parent, deps)) 51 | } 52 | 53 | /// Collect all the items registered as `collectable` into a `Collection` of that type. 54 | pub fn collect(&self) -> Result> { 55 | self.create(Collection::new()).map(|v| v.explode()) 56 | } 57 | 58 | pub fn when_ready(&mut self, action: F) 59 | where T: 'static + Any, 60 | F: for<'r> Fn(&Deps, &mut T) -> Result<()> + 'static + Send + Sync 61 | { 62 | match self.type_scope_created.entry(TypeId::of::()) { 63 | Entry::Occupied(mut list) => { 64 | list.get_mut().push(into_action_with_deps(action)); 65 | } 66 | Entry::Vacant(e) => { 67 | e.insert(vec![into_action_with_deps(action)]); 68 | } 69 | }; 70 | } 71 | 72 | /// Single dependency on a parent. 73 | pub fn attach(&mut self, constructor: F) 74 | where P: 'static + Any, // Parent 75 | C: 'static + Any, // Child 76 | F: for<'r> Fn(&Deps, &mut P) -> Result + 'static + Send + Sync 77 | { 78 | if TypeId::of::() == self.empty_type { 79 | self.register_isolated_constructor::

(into_isolated_constructor_with_ignored_child_deps(constructor)); 80 | } else { 81 | self.register_isolated_constructor::

(into_isolated_constructor_with_child_deps(constructor)); 82 | } 83 | } 84 | 85 | /// Single dependency on multiple parents. 86 | pub fn bridge(&mut self, constructor: F) 87 | where P1: 'static + Any + Send + Sync, // Parent 1 88 | P2: 'static + Any + Send + Sync, // Parent 2 89 | C: 'static + Any, // Child 90 | F: for<'r> Fn(&mut P1, &mut P2) -> Result + 'static + Send + Sync 91 | { 92 | // Get or insert inceptor that is used to manage P1 and P2 instances. 93 | let inceptor_1 = match self.inceptors 94 | .entry((TypeId::of::(), TypeId::of::())) { 95 | Entry::Occupied(entry) => { 96 | entry.get() 97 | .downcast_ref::>>>() 98 | .expect("expected to find Inceptor of correct type in map") 99 | .clone() 100 | } 101 | Entry::Vacant(entry) => { 102 | let arc = Arc::new(Mutex::new(if TypeId::of::() == self.empty_type { 103 | Inceptor::new_with_ignored_return_val(constructor) 104 | } else { 105 | Inceptor::new_with_return_val(constructor) 106 | })); 107 | entry.insert(Box::new(arc.clone())); 108 | arc 109 | } 110 | }; 111 | 112 | // Create inceptor clone for P2 instances 113 | let inceptor_2 = inceptor_1.clone(); 114 | 115 | self.register_shared_constructor::( 116 | into_shared_constructor::( 117 | inceptor_1, 118 | Box::new(|inceptor: &Arc>>, parent: &mut Box| 119 | { 120 | let parent_for_inceptor = parent.downcast_mut::>>() 121 | .expect("expected downcast P1") 122 | .clone(); 123 | inceptor.lock() 124 | .expect("failed to lock ic1") 125 | .incept_1(parent_for_inceptor) 126 | }), 127 | 1 128 | ) 129 | ); 130 | self.register_shared_constructor::( 131 | into_shared_constructor::( 132 | inceptor_2, 133 | Box::new(|inceptor: &Arc>>, parent: &mut Box| 134 | { 135 | let parent_for_inceptor = parent.downcast_mut::>>() 136 | .expect("expected downcast P2") 137 | .clone(); 138 | inceptor.lock() 139 | .expect("failed to lock ic2") 140 | .incept_2(parent_for_inceptor) 141 | }), 142 | 2 143 | ) 144 | ); 145 | } 146 | 147 | pub fn collectable(&mut self, constructor: F) 148 | where C: 'static + Any, 149 | F: for<'r> Fn(&Deps) -> C + 'static + Send + Sync 150 | { 151 | self.register_isolated_constructor::>( 152 | into_isolated_constructor_without_child_deps(move |deps: &Deps, parent: &mut Collection| { 153 | parent.push(constructor(deps)) 154 | }) 155 | ); 156 | } 157 | 158 | fn create_deps_for_any_parent(&self, 159 | type_id: TypeId, 160 | mut parent_not_shared: Box, 161 | to_shared: F) 162 | -> Result<(AnyInstance, Vec>)> 163 | where F: Fn(Box) -> Box 164 | { 165 | let mut deps = Vec::new(); 166 | 167 | // First, construct any instances that do not need parent wrapped in mutex 168 | 169 | match self.isolated_constructors.get(&type_id) { 170 | Some(isolated_list) => { 171 | for any_constructor in isolated_list { 172 | match any_constructor(&self, &mut parent_not_shared) { 173 | Ok(Constructed { children }) => deps.extend(children), 174 | Err(any_err) => return Err(any_err), 175 | }; 176 | } 177 | } 178 | None => (), 179 | } 180 | 181 | // Then, check if there are shared constructors, and if so, wrap value in mutex 182 | // and return it in AnyInstance::Shared, otherwise, return it in AnyInstance::Isolated. 183 | 184 | let mut parent_result = match self.shared_constructors.get(&type_id) { 185 | Some(shared_list) => { 186 | let mut parent_shared = to_shared(parent_not_shared); 187 | 188 | for any_constructor in shared_list { 189 | match any_constructor(&self, &mut parent_shared) { 190 | Ok(ConstructedShared { children }) => deps.extend(children), 191 | Err(any_err) => return Err(any_err), 192 | }; 193 | } 194 | 195 | AnyInstance::Shared(parent_shared) 196 | } 197 | None => AnyInstance::Isolated(parent_not_shared), 198 | }; 199 | 200 | // Execute post create actions for the value 201 | 202 | if let Some(actions) = self.type_scope_created.get(&type_id) { 203 | for action in actions { 204 | try!(action(&self, &mut parent_result)); 205 | } 206 | } 207 | 208 | Ok((parent_result, deps)) 209 | } 210 | 211 | /// Register child constructor that will be invoked when the parent `P` type is 212 | /// created. 213 | fn register_isolated_constructor(&mut self, 214 | any_constructor: Box) 215 | -> Result + Send + Sync>) { 216 | match self.isolated_constructors.entry(TypeId::of::

()) { 217 | Entry::Occupied(mut list) => { 218 | list.get_mut().push(any_constructor); 219 | } 220 | Entry::Vacant(e) => { 221 | e.insert(vec![any_constructor]); 222 | } 223 | }; 224 | } 225 | 226 | /// Register child constructor that will be invoked when the parent `P` type is 227 | /// created. 228 | fn register_shared_constructor(&mut self, 229 | any_constructor: Box) 230 | -> Result + Send + Sync>) { 231 | match self.shared_constructors.entry(TypeId::of::

()) { 232 | Entry::Occupied(mut list) => { 233 | list.get_mut().push(any_constructor); 234 | } 235 | Entry::Vacant(e) => { 236 | e.insert(vec![any_constructor]); 237 | } 238 | }; 239 | } 240 | } 241 | 242 | unsafe impl Send for Deps {} 243 | unsafe impl Sync for Deps {} 244 | 245 | fn into_action_with_deps(action: F) 246 | -> Box Result<()> + Send + Sync> 247 | where F: for<'r> Fn(&Deps, &mut P) -> Result<()> + 'static + Send + Sync, 248 | P: 'static + Any 249 | { 250 | Box::new(move |deps: &Deps, parent: &mut AnyInstance| -> Result<()> { 251 | match *parent { 252 | AnyInstance::Isolated(ref mut value) => { 253 | try!(action(deps, 254 | &mut value.downcast_mut::

() 255 | .expect("expected to downcast type in post create action"))) 256 | } 257 | AnyInstance::Shared(ref mut value) => { 258 | try!(action(deps, 259 | &mut value.downcast_mut::>>() 260 | .expect("expected to downcast type in post create action") 261 | .lock() 262 | .expect("expected to lock value for AnyInstance::Shared action"))) 263 | } 264 | }; 265 | Ok(()) 266 | }) 267 | } 268 | 269 | fn into_shared_constructor 270 | ( 271 | inceptor: Arc>>, 272 | incept_fun: Box>>, &mut Box) -> Result<(usize, Vec>)> + Send + Sync>, 273 | index: usize 274 | ) 275 | -> Box) -> Result + Send + Sync> 276 | where P1: 'static + Any + Send + Sync, // Parent 1 277 | P2: 'static + Any + Send + Sync, // Parent 2 278 | C: 'static + Any // Child 279 | { 280 | Box::new(move |deps: &Deps, parent: &mut Box| -> Result { 281 | let (id, instances) = try!(incept_fun(&inceptor, parent)); 282 | 283 | let mut children: Vec> = Vec::with_capacity(instances.len() + 1); 284 | 285 | for instance in instances { 286 | let instance_artifacts = 287 | try!(deps.create_deps_for_any_parent(TypeId::of::(), instance, to_shared::)); 288 | children.push(Box::new(instance_artifacts)); 289 | } 290 | 291 | children.push(Box::new(Destructor::new(inceptor.clone(), index, id))); 292 | 293 | Ok(ConstructedShared { children: children }) 294 | }) 295 | } 296 | 297 | fn into_isolated_constructor_with_child_deps 298 | (constructor: F) 299 | -> Box) -> Result + Send + Sync> 300 | where F: for<'r> Fn(&Deps, &mut P) -> Result + 'static + Send + Sync, 301 | P: 'static + Any, 302 | C: 'static + Any 303 | { 304 | Box::new(move |deps: &Deps, parent: &mut Box| -> Result { 305 | let child = { 306 | let concrete_parent = parent.downcast_mut::

() 307 | .expect("expected to downcast type in into_isolated_constructor_with_child_deps"); 308 | try!(deps.create(try!(constructor(deps, concrete_parent)))) 309 | }; 310 | Ok(Constructed { children: vec![Box::new(child)] }) 311 | }) 312 | } 313 | 314 | fn into_isolated_constructor_with_ignored_child_deps 315 | (constructor: F) 316 | -> Box) -> Result + Send + Sync> 317 | where F: for<'r> Fn(&Deps, &mut P) -> Result + 'static + Send + Sync, 318 | P: 'static + Any, 319 | C: 'static + Any 320 | { 321 | Box::new(move |deps: &Deps, parent: &mut Box| -> Result { 322 | try!(constructor(deps, 323 | parent.downcast_mut::

() 324 | .expect("expected to downcast type in \ 325 | into_isolated_constructor_with_ignored_child_deps"))); 326 | Ok(Constructed { children: vec![] }) 327 | }) 328 | } 329 | 330 | fn into_isolated_constructor_without_child_deps 331 | (constructor: F) 332 | -> Box) -> Result + Send + Sync> 333 | where F: for<'r> Fn(&Deps, &mut P) + 'static + Send + Sync, 334 | P: 'static + Any 335 | { 336 | Box::new(move |deps: &Deps, parent: &mut Box| -> Result { 337 | constructor(deps, 338 | parent.downcast_mut::

() 339 | .expect("expected to downcast type in \ 340 | into_isolated_constructor_without_child_deps")); 341 | Ok(Constructed { children: vec![] }) 342 | }) 343 | } 344 | 345 | #[cfg(test)] 346 | mod test { 347 | use Deps; 348 | use std::thread; 349 | use std::sync::{Arc, Mutex}; 350 | 351 | #[derive(Clone, Debug, Eq, PartialEq)] 352 | struct A(String); 353 | 354 | #[derive(Clone, Debug, Eq, PartialEq)] 355 | struct B(String); 356 | 357 | #[derive(Clone, Debug, Eq, PartialEq)] 358 | struct C(String); 359 | 360 | #[test] 361 | fn creates_dependency() { 362 | let mut deps = Deps::new(); 363 | 364 | // here we want to know what is the state of dependency in closure, hence 365 | // shared mutable reference to it 366 | let created_b_ref = Arc::new(Mutex::new(None)); 367 | 368 | deps.attach({ 369 | let created_b_ref = created_b_ref.clone(); 370 | move |_: &Deps, a: &mut A| { 371 | let b = B([&a.0[..], "+B"].concat()); 372 | *created_b_ref.lock().unwrap() = Some(b.clone()); 373 | Ok(b) 374 | } 375 | }); 376 | 377 | deps.create(A("Hello".into())).unwrap(); 378 | 379 | assert_eq!("Hello+B", 380 | (*created_b_ref.lock().unwrap()).clone().unwrap().0); 381 | } 382 | 383 | #[test] 384 | fn creates_dependency_of_dependency() { 385 | let mut deps = Deps::new(); 386 | 387 | // here we want to know what is the state of dependency in closure, hence 388 | // shared mutable reference to it 389 | let created_c_ref = Arc::new(Mutex::new(None)); 390 | 391 | deps.attach(|_: &Deps, a: &mut A| Ok(B([&a.0[..], "+B"].concat()))); 392 | 393 | deps.attach({ 394 | let created_c_ref = created_c_ref.clone(); 395 | move |_: &Deps, b: &mut B| { 396 | let c = C([&b.0[..], "+C"].concat()); 397 | *created_c_ref.lock().unwrap() = Some(c.clone()); 398 | Ok(c) 399 | } 400 | }); 401 | 402 | deps.create(A("Hello".into())).unwrap(); 403 | 404 | assert_eq!("Hello+B+C", 405 | (*created_c_ref.lock().unwrap()).clone().unwrap().0); 406 | } 407 | 408 | #[test] 409 | fn creates_mutable_dependency() { 410 | let mut deps = Deps::new(); 411 | 412 | deps.attach(|_: &Deps, a: &mut A| { 413 | *a = A("Hi!".into()); 414 | Ok(()) 415 | }); 416 | 417 | let mut a = deps.create(A("Hello".into())).unwrap(); 418 | let al = a.lock().unwrap(); 419 | 420 | assert_eq!("Hi!", al.0); 421 | } 422 | 423 | #[test] 424 | fn should_work_accross_threads() { 425 | let mut deps = Deps::new(); 426 | 427 | deps.attach(|_: &Deps, _: &mut A| Ok(B("b".into()))); 428 | deps.attach(|_: &Deps, _: &mut B| Ok(C("c".into()))); 429 | 430 | let dep_refs = Arc::new(deps); 431 | 432 | let a = thread::spawn({ 433 | let a_deps = dep_refs.clone(); 434 | move || a_deps.create(A("a".into())).unwrap() 435 | }); 436 | 437 | let b = thread::spawn({ 438 | let b_deps = dep_refs.clone(); 439 | move || b_deps.create(B("b".into())).unwrap() 440 | }); 441 | 442 | assert_eq!(b.join().unwrap().explode(), B("b".into())); 443 | assert_eq!(a.join().unwrap().explode(), A("a".into())); 444 | } 445 | 446 | #[test] 447 | fn can_create_bridge_dependency() { 448 | let mut deps = Deps::new(); 449 | 450 | let created_bridge = Arc::new(Mutex::new(None)); 451 | let created_bridge_clone = created_bridge.clone(); // so we can modify this from inside the closure 452 | 453 | deps.bridge(|a: &mut A, b: &mut B| Ok(vec![a.0.clone(), b.0.clone()])); 454 | 455 | // Use this to copy created Vec value from bridge to mutex protected clone 456 | deps.when_ready(move |_: &Deps, parent: &mut Vec| { 457 | *created_bridge_clone.lock().unwrap() = Some(parent.clone()); 458 | Ok(()) 459 | }); 460 | 461 | // Bind to created A and modify the value from "Hello" to "Hi" 462 | deps.attach(|_: &Deps, a: &mut A| { 463 | *a = A("Hi".into()); 464 | Ok(5) 465 | }); 466 | 467 | // Attach to any type Vec and append "Nice" to last element 468 | deps.attach(|_: &Deps, created_bridge_result: &mut Vec| { 469 | created_bridge_result.push("Nice".to_string()); 470 | Ok(()) 471 | }); 472 | 473 | // Create both instigators and result should appear 474 | let mut a = deps.create(A("Hello".into())).unwrap(); 475 | let mut b = deps.create(B("World".into())).unwrap(); 476 | 477 | { 478 | let al = a.lock().unwrap(); 479 | let bl = b.lock().unwrap(); 480 | 481 | assert_eq!("Hi", al.0); 482 | assert_eq!("World", bl.0); 483 | } 484 | 485 | { 486 | let val = created_bridge.lock() 487 | .unwrap(); 488 | assert_eq!("HiWorldNice", 489 | val.as_ref().expect("expected bridge val to be created").concat()); 490 | } 491 | 492 | let mut c = deps.create(B("Rust".into())).unwrap(); 493 | 494 | { 495 | let cl = c.lock().unwrap(); 496 | 497 | assert_eq!("Rust", cl.0); 498 | } 499 | 500 | { 501 | let val = created_bridge.lock() 502 | .unwrap(); 503 | assert_eq!("HiRustNice", 504 | val.as_ref().expect("expected bridge val to be created").concat()); 505 | } 506 | 507 | assert_eq!(c.explode(), B("Rust".into())); 508 | assert_eq!(a.explode(), A("Hi".into())); 509 | assert_eq!(b.explode(), B("World".into())); 510 | } 511 | } 512 | --------------------------------------------------------------------------------