1 // Copyright Orfeo Da Via 2014. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 module endovena.container; 7 import std.conv : to; 8 import std.functional : toDelegate; 9 import std.stdio; 10 import std.traits; 11 12 import std.algorithm; 13 import std.array; 14 import std.range; 15 16 import endovena.provider; 17 import endovena.reuse; 18 19 interface Module { 20 void configure(Container dejector); 21 } 22 23 class Container { 24 private Binding[] bindings; 25 private Reuse[string] scopes; 26 27 this(Module[] modules) { 28 this.bindReuse!Transient; 29 this.bindReuse!Singleton; 30 31 foreach(module_; modules) { 32 module_.configure(this); 33 } 34 } 35 36 this() { 37 this([]); 38 } 39 40 void bindReuse(Class)() { 41 immutable key = fullyQualifiedName!Class; 42 assert(key.length > 0); 43 if (key in this.scopes) { 44 throw new Exception("Scope already bound"); 45 } 46 this.scopes[key] = new Class(); 47 } 48 49 void register(C, R: Reuse = Transient)() { 50 this.register!(C, C, R)(""); 51 } 52 53 void register(C, R: Reuse = Transient)(C instance) { 54 this.register!(C, R)(new InstanceProvider(instance), ""); 55 } 56 57 void register(I, C, R: Reuse = Transient)() { 58 this.register!(I, R)(new ClassProvider!C(this), ""); 59 } 60 61 void register(I, R: Reuse = Transient)(Provider provider) { 62 this.register!(I, R)(provider, ""); 63 } 64 65 void register(I, R: Reuse = Transient)(Object delegate() provide) { 66 this.register!(I, R)(new FunctionProvider(provide), ""); 67 } 68 69 void register(I, R: Reuse = Transient)(Object delegate(Container) factory) { 70 this.register!(I, R)(new FactoryProvider(this, factory), ""); 71 } 72 73 void registerFunc(I, R: Reuse = Transient)(Object function() provide) { 74 this.register!(I, R)(toDelegate(provide)); 75 } 76 77 void register(C, R: Reuse = Transient)(string name) { 78 this.register!(C, C, R)(name); 79 } 80 81 void register(I, C, R: Reuse = Transient)(string name) { 82 this.register!(I, R)(new ClassProvider!C(this), name); 83 } 84 85 void register(I, R: Reuse = Transient)(Provider provider, string name) { 86 if (exists!I(name)) { 87 throw new RegistrationException("Interface already bound", fullyQualifiedName!I); 88 } 89 this.bindings ~= createBinding!(I, R)(provider, name); 90 } 91 92 void register(I, R: Reuse = Transient)(Object delegate() provide, string name) { 93 this.register!(I, R)(new FunctionProvider(provide), name); 94 } 95 96 void register(I, R: Reuse = Transient)(Object delegate(Container) factory, 97 string name) { 98 this.register!(I, R)(new FactoryProvider(this, factory), name); 99 } 100 101 private bool exists(I)(string name) { 102 return !filterExactly!I(name).empty; 103 } 104 105 private Binding createBinding(I, R)(Provider provider, string name) { 106 auto reuse = this.scopes[fullyQualifiedName!R]; 107 return Binding(fullyQualifiedName!I 108 , name 109 , provider 110 , reuse); 111 } 112 113 I get(I)(string name) { 114 Binding[] binding = filterExactly!I(name); 115 if (binding.empty) { 116 throw new ResolveException("Type not registered." 117 , fullyQualifiedName!I 118 , name); 119 } 120 return resolve!I(binding[0]); 121 } 122 123 124 T get(T)() { 125 static if(is(T t == I[], I)) { 126 I[] array; 127 Binding[] filtered = filterByInterface!I; 128 if (filtered.empty) { 129 throw new ResolveException("Type not registered.", fullyQualifiedName!T); 130 } 131 132 foreach (Binding binding; filtered) { 133 array ~= resolve!I(binding); 134 } 135 return array; 136 } else { 137 Binding[] binding = filterExactly!T(""); 138 if (binding.empty) { 139 throw new ResolveException("Type not registered.", fullyQualifiedName!T); 140 } 141 return resolve!T(binding[0]); 142 } 143 } 144 145 private Binding[] filterByInterface(I)() { 146 string requestedFQN = fullyQualifiedName!I; 147 return bindings.filter!(a => a.fqn == requestedFQN).array; 148 } 149 150 private Binding[] filterExactly(I)(string name) { 151 string requestedFQN = fullyQualifiedName!I; 152 return bindings.filter!(a => a.fqn == requestedFQN && a.name == name).array; 153 } 154 155 private I resolve(I)(Binding binding) { 156 string key = fullyQualifiedName!I; 157 if (binding.name.length > 0) { 158 key ~= binding.name; 159 } 160 return cast(I) binding.reuse.get(key, binding.provider); 161 } 162 163 I delegate() getDelegate(I)() { 164 return delegate() { return this.get!I; }; 165 } 166 } 167 168 import std.string; 169 class ResolveException: Exception { 170 this(string message, string key, string name) { 171 super(format("Exception while resolving type %s named %s: %s", key, name, message)); 172 } 173 this(string message, string key) { 174 super(format("Exception while resolving type %s: %s", key, message)); 175 } 176 } 177 178 class RegistrationException : Exception { 179 this(string message, string key) { 180 super(format("Exception while registering type %s: %s", key, message)); 181 } 182 } 183 184 package struct Binding { 185 string fqn; // fullyQualifiedName 186 string name; // name in named instance 187 Provider provider; 188 Reuse reuse; 189 }