1 /** 2 This module implements annotation based configuration of containers. 3 4 License: 5 Boost Software License - Version 1.0 - August 17th, 2003 6 7 Permission is hereby granted, free of charge, to any person or organization 8 obtaining a copy of the software and accompanying documentation covered by 9 this license (the "Software") to use, reproduce, display, distribute, 10 execute, and transmit the Software, and to prepare derivative works of the 11 Software, and to permit third-parties to whom the Software is furnished to 12 do so, all subject to the following: 13 14 The copyright notices in the Software and this entire statement, including 15 the above license grant, this restriction and the following disclaimer, 16 must be included in all copies of the Software, in whole or in part, and 17 all derivative works of the Software, unless such copies or derivative 18 works are solely in the form of machine-executable object code generated by 19 a source language processor. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 DEALINGS IN THE SOFTWARE. 28 29 Authors: 30 Alexandru Ermicioi 31 **/ 32 module aermicioi.aedi.configurer.annotation.annotation; 33 34 public import aermicioi.aedi.factory.reference : lref, anonymous; 35 36 import aermicioi.aedi.storage.locator; 37 import aermicioi.aedi.storage.storage; 38 import aermicioi.aedi.storage.wrapper; 39 import aermicioi.aedi.container.container; 40 import aermicioi.aedi.factory.factory; 41 import aermicioi.aedi.factory.reference; 42 import aermicioi.aedi.factory.generic_factory; 43 import aermicioi.aedi.factory.proxy_factory; 44 import aermicioi.aedi.exception; 45 import aermicioi.util.traits; 46 47 import std.traits; 48 import std.meta; 49 import std.typecons; 50 import std.conv : to; 51 import std.algorithm; 52 53 /** 54 Annotation used to denote an aggregate that should be stored into an container. 55 **/ 56 struct ComponentAnnotation { 57 58 /** 59 Constructs a factory for aggregate of type T 60 61 Params: 62 T = the aggregate type 63 locator = locator used to extract needed dependencies for T 64 65 Returns: 66 GenericFactory!T for objects 67 GenericFactory!(Wrapper!T) for structs 68 **/ 69 GenericFactory!T factory(T)(Locator!() locator) { 70 return new GenericFactoryImpl!(T)(locator); 71 } 72 } 73 74 /** 75 ditto 76 **/ 77 alias component = ComponentAnnotation; 78 79 /** 80 Annotation used to mark a constructor to be used for aggregate instantiation. 81 82 Params: 83 Args = tuple of argument types for arguments to be passed into a constructor. 84 **/ 85 struct ConstructorAnnotation(Args...) { 86 Tuple!Args args; 87 88 /** 89 Constructor accepting a list of arguments, that will be passed to constructor. 90 91 Params: 92 args = arguments passed to aggregate's constructor 93 **/ 94 this(Args args) { 95 this.args = args; 96 } 97 98 /** 99 Constructs a constructor based factory for aggregate of type T 100 101 Params: 102 T = the aggregate type 103 locator = locator used to extract needed dependencies for T 104 105 Returns: 106 InstanceFactory!T for objects 107 InstanceFactory!(Wrapper!T) for structs 108 **/ 109 InstanceFactory!T factoryContainer(T, string property)(Locator!() locator) { 110 auto constructor = new ConstructorBasedFactory!(T, Args)(args.expand); 111 constructor.locator = locator; 112 113 return constructor; 114 } 115 } 116 117 /** 118 ditto 119 **/ 120 auto constructor(Args...)(Args args) { 121 return ConstructorAnnotation!Args(args); 122 } 123 124 /** 125 Annotation used to mark a member to be called or set (in case of fields), with args passed to setter. 126 127 Note: if an overloaded method is annotated with Setter, the method from overload set that matches argument list in Setter annotation 128 will be called. 129 130 Params: 131 Args = the argument types of arguments passed to method 132 **/ 133 struct SetterAnnotation(Args...) { 134 Tuple!Args args; 135 136 /** 137 Constructor accepting a list of arguments, that will be passed to method, or set to a field. 138 139 Params: 140 args = arguments passed to aggregate's constructor 141 **/ 142 this(Args args) { 143 this.args = args; 144 } 145 146 /** 147 Constructs a configurer that will call or set a member for aggregate of type T. 148 149 Constructs a configurer that will call or set a member for aggregate of type T. 150 In case when member is a method, it will be called with passed arguments. 151 If method is an overload set, the method that matches argument list will be called. 152 In case when member is a field, it will be set to first argument from Args list. 153 154 Params: 155 T = the aggregate type 156 method = the member which setter will call or set. 157 locator = locator used to extract needed dependencies for T 158 159 Returns: 160 PropertyConfigurer!T for objects 161 PropertyConfigurer!(Wrapper!T) for structs 162 **/ 163 PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator) 164 if ( 165 !isField!(T, method) 166 ) { 167 mixin assertObjectMethodCompatible!(T, method, Args); 168 169 auto method = new MethodConfigurer!(T, method, Args)(args.expand); 170 method.locator = locator; 171 172 return method; 173 } 174 175 /** 176 ditto 177 **/ 178 PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator) 179 if ( 180 isField!(T, method) && 181 (Args.length == 1) 182 ) { 183 mixin assertFieldCompatible!(T, method, Args); 184 185 auto method = new FieldConfigurer!(T, method, Args[0])(args[0]); 186 method.locator = locator; 187 188 return method; 189 } 190 } 191 192 /** 193 ditto 194 **/ 195 auto setter(Args...)(Args args) { 196 return SetterAnnotation!Args(args); 197 } 198 199 /** 200 Annotation that specifies a delegate to be used to instantiate aggregate. 201 202 Params: 203 Z = the type of aggregate that will be returned by the delegate 204 Args = type tuple of args that can be passed to delegate. 205 **/ 206 struct CallbackFactoryAnnotation(Z, Dg, Args...) 207 if ((is(Dg == Z delegate (Locator!(), Args)) || is(Dg == Z function (Locator!(), Args)))) { 208 Tuple!Args args; 209 Dg dg; 210 211 /** 212 Constructor accepting a factory delegate, and it's arguments. 213 214 Params: 215 dg = delegate that will factory an aggregate 216 args = list of arguments passed to delegate. 217 **/ 218 this(Dg dg, ref Args args) { 219 this.dg = dg; 220 this.args = tuple(args); 221 } 222 223 /** 224 Constructs a factory that uses delegate to instantiate an aggregate of type T. 225 226 Params: 227 T = the aggregate type 228 locator = locator used to extract needed dependencies for T, it is also passed to delegate as first argument. 229 230 Returns: 231 InstanceFactory!T for objects 232 InstanceFactory!(Wrapper!T) for structs 233 **/ 234 InstanceFactory!T factoryContainer(T, string p = "")(Locator!() locator) 235 if (is(Z : T)) { 236 auto callback = new CallbackFactory!(T, Dg, Args)(dg, args.expand); 237 callback.locator = locator; 238 239 return callback; 240 } 241 } 242 243 /** 244 ditto 245 **/ 246 auto fact(T, Args...)(T delegate(Locator!(), Args) dg, Args args) { 247 return CallbackFactoryAnnotation!(T, T delegate(Locator!(), Args), Args)(dg, args); 248 } 249 250 /** 251 ditto 252 **/ 253 auto fact(T, Args...)(T function(Locator!(), Args) dg, Args args) { 254 return CallbackFactoryAnnotation!(T, T function(Locator!(), Args), Args)(dg, args); 255 } 256 257 /** 258 Annotation that specifies a delegate to be used to configure aggregate somehow. 259 260 Params: 261 Z = the type of aggregate that will be returned by the delegate 262 Args = type tuple of args that can be passed to delegate. 263 **/ 264 struct CallbackConfigurerAnnotation(Z, Dg, Args...) 265 if ( 266 is(Dg == void delegate (Locator!(), Z, Args)) || 267 is(Dg == void function (Locator!(), Z, Args)) || 268 is(Dg == void delegate (Locator!(), ref Z, Args)) || 269 is(Dg == void function (Locator!(), ref Z, Args)) 270 ){ 271 Tuple!Args args; 272 Dg dg; 273 274 /** 275 Constructor accepting a configurer delegate, and it's arguments. 276 277 Params: 278 dg = delegate that will be used to configure an aggregate 279 args = list of arguments passed to delegate. 280 **/ 281 this(Dg dg, ref Args args) { 282 this.dg = dg; 283 this.args = tuple(args); 284 } 285 286 /** 287 Constructs a configurer that uses delegate to configure an aggregate of type T. 288 289 Params: 290 T = the aggregate type 291 locator = locator that can be used by delegate to extract some custom data. 292 293 Returns: 294 PropertyConfigurer!T for objects 295 PropertyConfigurer!(Wrapper!T) for structs 296 **/ 297 PropertyConfigurer!T factoryConfigurer(T, string p = "")(Locator!() locator) 298 if (is(T : Z)) { 299 auto callback = new CallbackConfigurer!(T, Dg, Args)(dg, args.expand); 300 callback.locator = locator; 301 302 return callback; 303 } 304 } 305 306 /** 307 ditto 308 **/ 309 auto callback(T, Args...)(void delegate (Locator!(), ref T, Args) dg, Args args) { 310 return CallbackConfigurerAnnotation!(T, void delegate (Locator!(), ref T, Args), Args)(dg, args); 311 } 312 313 /** 314 ditto 315 **/ 316 auto callback(T, Args...)(void function (Locator!(), ref T, Args) dg, Args args) { 317 return CallbackConfigurerAnnotation!(T, void function (Locator!(), ref T, Args), Args)(dg, args); 318 } 319 320 /** 321 ditto 322 **/ 323 auto callback(T, Args...)(void delegate (Locator!(), T, Args) dg, Args args) { 324 return CallbackConfigurerAnnotation!(T, void delegate (Locator!(), T, Args), Args)(dg, args); 325 } 326 327 /** 328 ditto 329 **/ 330 auto callback(T, Args...)(void function (Locator!(), T, Args) dg, Args args) { 331 return CallbackConfigurerAnnotation!(T, void function (Locator!(), T, Args), Args)(dg, args); 332 } 333 334 /** 335 Annotation used to mark constructor or method for auto wiring. 336 337 Marking a method/constructor with autowired annotation will make container to call it with arguments fetched from 338 locator by types of them. 339 340 Note: even if a method/constructor from an overloaded set is marked with autowired annotation, the first method from overload set 341 will be used. Due to that autowired annotation is recommended to use on methods/constrcutors that are not overloaded. 342 343 **/ 344 struct AutowiredAnnotation { 345 PropertyConfigurer!T factoryConfigurer(T, string method)(Locator!() locator) 346 if ( 347 !isField!(T, method) && 348 isSomeFunction!(getMember!(T, method)) 349 ) { 350 351 alias params = Parameters!(__traits(getOverloads, T, method)[0]); 352 auto references = tuple(staticMap!(toLref, params)); 353 354 auto method = new MethodConfigurer!(T, method, staticMap!(toLrefType, params))(references.expand); 355 method.locator = locator; 356 357 return method; 358 } 359 360 PropertyConfigurer!T factoryConfigurer(T, string property)(Locator!() locator) 361 if ( 362 isField!(T, property) 363 ) { 364 365 alias paramType = typeof(getMember!(T, property)); 366 367 auto lref = toLref!paramType; 368 auto method = new FieldConfigurer!(T, property, toLrefType!paramType)(lref); 369 method.locator = locator; 370 371 return method; 372 } 373 374 InstanceFactory!T factoryContainer(T, string property)(Locator!() locator) { 375 alias params = Parameters!(__traits(getOverloads, T, "__ctor")[0]); 376 auto references = tuple(staticMap!(toLref, params)); 377 378 auto method = new ConstructorBasedFactory!(T, staticMap!(toLrefType, params))(references.expand); 379 method.locator = locator; 380 381 return method; 382 } 383 } 384 385 /** 386 ditto 387 **/ 388 alias autowired = AutowiredAnnotation; 389 390 /** 391 An annotation used to provide custom identity for an object in container. 392 **/ 393 struct QualifierAnnotation { 394 string id; 395 } 396 397 /** 398 An annotation used to provide custom identity for an object in container. 399 400 This function is a convenince function to automatically infer required types for underlying annotation. 401 402 Params: 403 id = identity of object in container 404 **/ 405 auto qualifier(string id) { 406 return QualifierAnnotation(id); 407 } 408 409 /** 410 ditto 411 **/ 412 QualifierAnnotation qualifier(string id)() { 413 return QualifierAnnotation(id); 414 } 415 416 /** 417 An annotation used to provide custom identity for an object in container by some interface. 418 419 This function is a convenince function to automatically infer required types for underlying annotation. 420 421 Params: 422 I = identity of object in container 423 **/ 424 QualifierAnnotation qualifier(I)() { 425 return QualifierAnnotation(name!I); 426 } 427 428 /** 429 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store. 430 **/ 431 struct ContainedAnnotation { 432 string id; 433 } 434 435 /** 436 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store. 437 438 This function is a convenince function to automatically infer required types for underlying annotation. 439 440 Params: 441 id = identity of container where to store the object. 442 **/ 443 auto contained(string id) { 444 return ContainedAnnotation(id); 445 }