├── .gitignore ├── .rustfmt.toml ├── src ├── lib.rs ├── typemap.rs ├── event.rs └── di.rs ├── README.md └── Cargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(trait_alias)] 2 | 3 | pub mod di; 4 | pub mod event; 5 | pub mod typemap; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Types Over Strings: Extensible Architectures in Rust 2 | 3 | Materials accompanying the note ["Types Over Strings: Extensible Architectures in Rust"](http://willcrichton.net/notes/types-over-strings/). Run `cargo test` to run the tests. 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "typepatterns" 3 | version = "0.1.0" 4 | authors = ["Will Crichton "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /src/typemap.rs: -------------------------------------------------------------------------------- 1 | use std::any::{Any, TypeId}; 2 | use std::collections::HashMap; 3 | 4 | pub struct TypeMap(HashMap>); 5 | 6 | impl TypeMap { 7 | pub fn new() -> TypeMap { 8 | TypeMap(HashMap::new()) 9 | } 10 | 11 | pub fn has(&self) -> bool { 12 | self.0.contains_key(&TypeId::of::()) 13 | } 14 | 15 | pub fn get(&self) -> Option<&T> { 16 | self 17 | .0 18 | .get(&TypeId::of::()) 19 | .map(|t| t.downcast_ref::().unwrap()) 20 | } 21 | 22 | pub fn get_mut(&mut self) -> Option<&mut T> { 23 | self 24 | .0 25 | .get_mut(&TypeId::of::()) 26 | .map(|t| t.downcast_mut::().unwrap()) 27 | } 28 | 29 | pub fn set(&mut self, t: T) { 30 | self.0.insert(TypeId::of::(), Box::new(t)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/event.rs: -------------------------------------------------------------------------------- 1 | use crate::typemap::TypeMap; 2 | 3 | pub trait Event: 'static {} 4 | pub trait EventListener = FnMut(&E) -> () + 'static; 5 | 6 | type ListenerVec = Vec>>; 7 | 8 | pub struct EventDispatcher(TypeMap); 9 | 10 | impl EventDispatcher { 11 | pub fn new() -> EventDispatcher { 12 | EventDispatcher(TypeMap::new()) 13 | } 14 | 15 | pub fn add_event_listener(&mut self, f: F) 16 | where 17 | E: Event, 18 | F: EventListener, 19 | { 20 | if !self.0.has::>() { 21 | self.0.set::>(Vec::new()); 22 | } 23 | 24 | let listeners = self.0.get_mut::>().unwrap(); 25 | listeners.push(Box::new(f)); 26 | } 27 | 28 | pub fn trigger(&mut self, event: &E) { 29 | if let Some(listeners) = self.0.get_mut::>() { 30 | for callback in listeners { 31 | callback(event); 32 | } 33 | } 34 | } 35 | } 36 | 37 | #[cfg(test)] 38 | mod test { 39 | use super::*; 40 | 41 | struct OnClick { 42 | mouse_x: i32, 43 | mouse_y: i32, 44 | } 45 | 46 | impl Event for OnClick {} 47 | 48 | #[test] 49 | fn basic() { 50 | let mut node = EventDispatcher::new(); 51 | node.add_event_listener(|event: &OnClick| { 52 | assert_eq!(event.mouse_x, 10); 53 | assert_eq!(event.mouse_y, 5); 54 | }); 55 | node.trigger(&OnClick { 56 | mouse_x: 10, 57 | mouse_y: 5, 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/di.rs: -------------------------------------------------------------------------------- 1 | use crate::typemap::TypeMap; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | pub type DIObj = Arc>; 5 | 6 | pub trait GetInput: Sized { 7 | fn get_input(manager: &DIManager) -> Option; 8 | } 9 | 10 | pub trait DIBuilder { 11 | type Input: GetInput; 12 | type Output: 'static; 13 | 14 | fn build(input: Self::Input) -> Self::Output; 15 | } 16 | 17 | pub struct DIManager(TypeMap); 18 | 19 | impl DIManager { 20 | pub fn new() -> DIManager { 21 | DIManager(TypeMap::new()) 22 | } 23 | 24 | pub fn build(&mut self) -> Option> { 25 | let input = T::Input::get_input(self)?; 26 | let obj = T::build(input); 27 | let sync_obj = Arc::new(Mutex::new(obj)); 28 | self.0.set::>(sync_obj.clone()); 29 | Some(sync_obj) 30 | } 31 | } 32 | 33 | impl GetInput for DIObj { 34 | fn get_input(manager: &DIManager) -> Option { 35 | manager.0.get::().map(|obj| obj.clone()) 36 | } 37 | } 38 | 39 | impl GetInput for () { 40 | fn get_input(_manager: &DIManager) -> Option { 41 | Some(()) 42 | } 43 | } 44 | 45 | impl GetInput for (T,) { 46 | fn get_input(manager: &DIManager) -> Option { 47 | T::get_input(manager).map(|t| (t,)) 48 | } 49 | } 50 | 51 | impl GetInput for (S, T) { 52 | fn get_input(manager: &DIManager) -> Option { 53 | S::get_input(manager) 54 | .and_then(|s| T::get_input(manager).and_then(|t| Some((s, t)))) 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod test { 60 | use super::*; 61 | 62 | trait Database { 63 | fn name(&self) -> &'static str; 64 | } 65 | 66 | struct MySQL; 67 | struct Postgres; 68 | impl Database for MySQL { 69 | fn name(&self) -> &'static str { 70 | "MySQL" 71 | } 72 | } 73 | impl Database for Postgres { 74 | fn name(&self) -> &'static str { 75 | "Postgres" 76 | } 77 | } 78 | 79 | impl DIBuilder for MySQL { 80 | type Input = (); 81 | type Output = Box; 82 | fn build((): ()) -> Box { 83 | Box::new(MySQL) 84 | } 85 | } 86 | 87 | impl DIBuilder for Postgres { 88 | type Input = (); 89 | type Output = Box; 90 | fn build((): ()) -> Box { 91 | Box::new(Postgres) 92 | } 93 | } 94 | 95 | struct WebServer { 96 | db: DIObj>, 97 | } 98 | 99 | impl DIBuilder for WebServer { 100 | type Input = (DIObj>,); 101 | type Output = WebServer; 102 | 103 | fn build((db,): Self::Input) -> WebServer { 104 | WebServer { db } 105 | } 106 | } 107 | 108 | impl WebServer { 109 | fn run(&self) { 110 | println!("Db name: {}", self.db.lock().unwrap().name()); 111 | } 112 | } 113 | 114 | #[test] 115 | fn basic() { 116 | let mut manager = DIManager::new(); 117 | manager.build::().unwrap(); 118 | let server = manager.build::().unwrap(); 119 | server.lock().unwrap().run(); 120 | } 121 | } 122 | --------------------------------------------------------------------------------