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 pragma(msg, T, ".", property); 368 auto lref = toLref!paramType; 369 auto method = new FieldConfigurer!(T, property, toLrefType!paramType)(lref); 370 method.locator = locator; 371 372 return method; 373 } 374 375 InstanceFactory!T factoryContainer(T, string property)(Locator!() locator) { 376 alias params = Parameters!(__traits(getOverloads, T, "__ctor")[0]); 377 auto references = tuple(staticMap!(toLref, params)); 378 379 auto method = new ConstructorBasedFactory!(T, staticMap!(toLrefType, params))(references.expand); 380 method.locator = locator; 381 382 return method; 383 } 384 } 385 386 /** 387 ditto 388 **/ 389 alias autowired = AutowiredAnnotation; 390 391 /** 392 An annotation used to provide custom identity for an object in container. 393 **/ 394 struct QualifierAnnotation { 395 string id; 396 } 397 398 /** 399 An annotation used to provide custom identity for an object in container. 400 401 This function is a convenince function to automatically infer required types for underlying annotation. 402 403 Params: 404 id = identity of object in container 405 **/ 406 auto qualifier(string id) { 407 return QualifierAnnotation(id); 408 } 409 410 /** 411 An annotation used to provide custom identity for an object in container by some interface. 412 413 This function is a convenince function to automatically infer required types for underlying annotation. 414 415 Params: 416 I = identity of object in container 417 **/ 418 auto qualifier(I)() { 419 return QualifierAnnotation(name!I); 420 } 421 422 /** 423 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store. 424 **/ 425 struct ContainedAnnotation { 426 string id; 427 } 428 429 /** 430 When objects are registered into an aggregate container, this annotation marks in which sub-container it is required to store. 431 432 This function is a convenince function to automatically infer required types for underlying annotation. 433 434 Params: 435 id = identity of container where to store the object. 436 **/ 437 auto contained(string id) { 438 return ContainedAnnotation(id); 439 }