1 /** 2 Provides annotation based configuration and registration for components. 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 aermicioi 31 **/ 32 module aermicioi.aedi.configurer.annotation.component_scan; 33 34 import aermicioi.aedi.configurer.annotation.annotation; 35 import aermicioi.aedi.storage.locator; 36 import aermicioi.aedi.storage.storage; 37 import aermicioi.aedi.storage.alias_aware : AliasAware; 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.aedi.util.traits; 46 import aermicioi.aedi.factory.wrapping_factory : WrappingFactory; 47 48 import std.traits; 49 import std.meta; 50 import std.conv : to; 51 import std.algorithm; 52 import std.experimental.logger; 53 54 /** 55 Check if a type T is a factory policy. 56 57 Check if a type T is a factory policy. 58 A factory policy is a policy that based on type X, can 59 create a factory for type X. 60 **/ 61 enum bool isFactoryPolicy(T, X = Object) = is(T == struct) && is(typeof(&T.createFactory!X) : Z function(Locator!()), Z : Factory!X); 62 63 /** 64 Check if a type T is a configurator policy. 65 66 Check if a type T is a configurator policy. 67 A configurator policy is a policy that given a type 68 X can create InstanceFactory, InstanceDestructor, or PropertyConfigurer 69 instances and add them to a factory that implements GenericFactory!X interface, as well 70 as modify other properties exposed by GenericFactory!X interface. 71 **/ 72 enum bool isConfiguratorPolicy(T, X : GenericFactory!Z = GenericFactory!Object, Z) = 73 is(T == struct) && 74 is(typeof(&T.configure!(X)) : void function(M, Locator!()), M : X); 75 76 /** 77 Create a GenericFactory!T if T is annotated with @component annotation. 78 **/ 79 @safe struct GenericFactoryPolicy { 80 /** 81 Create a GenericFactory!T if T is annotated with @component annotation. 82 83 Params: 84 locator = locator used by factory to fetch it's dependencies 85 86 Returns: 87 GenericFactory!T 88 **/ 89 static GenericFactory!T createFactory(T)(Locator!() locator) { 90 GenericFactory!T factory; 91 92 alias Components = allUDAs!T; 93 static foreach (index; 0 .. Components.length) { 94 static if (isComponentAnnotation!(Components[index])) { 95 debug(annotationScanDebug) trace(typeid(T), " is marked with @component annotation, creating a component factory for it."); 96 factory = new GenericFactoryImpl!T(locator); 97 } 98 } 99 100 return factory; 101 } 102 } 103 104 /** 105 A factory policy that uses annotations implementing factory policy interface on component to instantiate the component. 106 **/ 107 @safe struct GenericFactoryAnnotationPolicy { 108 109 /** 110 Create a component factory for T using annotations on it that implement factory policy interface. 111 112 Params: 113 locator = locator of components used by component factory 114 115 Returns: 116 A component factory 117 **/ 118 static GenericFactory!T createFactory(T)(Locator!() locator) { 119 120 GenericFactory!T factory; 121 122 alias FactoryPolicies = allUDAs!T; 123 124 static foreach (index; 0 .. FactoryPolicies.length) { 125 static if (isFactoryPolicy!(toType!(FactoryPolicies[index]), T)) { 126 factory = FactoryPolicies[index].createFactory!T(locator); 127 } 128 } 129 130 return factory; 131 } 132 } 133 134 /** 135 A factory policy that applies in order a set of factory policies to create component factory. 136 **/ 137 @safe struct FallbackFactoryPolicy(FactoryPolicies...) 138 if (allSatisfy!(isFactoryPolicy, FactoryPolicies)) { 139 140 /** 141 Create component factory using one of supplied factory policies 142 143 Params: 144 locator = locator of components used by component factory 145 146 Returns: 147 A component factory, from the first factory policy that returned an instance 148 **/ 149 static GenericFactory!T createFactory(T)(Locator!() locator) { 150 151 static foreach (FactoryPolicy; FactoryPolicies) {{ 152 GenericFactory!T factory = FactoryPolicy.createFactory!T(locator); 153 154 if (factory !is null) { 155 return factory; 156 } 157 }} 158 159 return null; 160 } 161 } 162 163 /** 164 Chain a set of configurator policies. 165 **/ 166 @safe struct ChainedConfiguratorPolicy(ConfiguratorPolicies...) 167 if (allSatisfy!(isConfiguratorPolicy, ConfiguratorPolicies)) { 168 169 /** 170 Run ConfiguratorPolicies on instantiator in sequential manner 171 172 Params: 173 instantiator = factory which is configured by ConfiguratorPolicies 174 locator = locator used by factory 175 176 **/ 177 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 178 179 foreach (policy; ConfiguratorPolicies) { 180 policy.configure(instantiator, locator); 181 } 182 } 183 } 184 185 /** 186 Set allocator used by factory to instantiate component T. 187 **/ 188 @safe struct AllocatorConfiguratorPolicy { 189 190 /** 191 Set allocator from @allocator annotation into GenericFactory!Z. 192 193 Params: 194 instantiator = factory which is set allocator from @allocator annotation 195 locator = locator used by factory 196 **/ 197 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 198 199 alias Allocators = allUDAs!Z; 200 static foreach (index; 0 .. Allocators.length) { 201 static if (isAllocatorAnnotation!(Allocators[index])) { 202 debug(annotationScanDebug) trace(typeid(Z), " is marked with @allocator annotation, supplying component factory with custom allocator ", Allocators[index].allocator); 203 instantiator.allocator = Allocators[index].iallocator; 204 } 205 } 206 } 207 } 208 209 /** 210 Set callback instance factory from @callbac annotation into GenericFactory!Z 211 **/ 212 @safe struct CallbackFactoryConfiguratorPolicy { 213 /** 214 Set callback instance factory from @callback annotation into GenericFactory!Z. 215 216 Params: 217 instantiator = factory which is set callback instance factory from @callback annotation 218 locator = locator used by factory 219 **/ 220 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 221 alias CallbackFactories = allUDAs!Z; 222 223 static foreach (index; 0 .. CallbackFactories.length) { 224 static if (isCallbackFactoryAnnotation!(CallbackFactories[index])) { 225 debug(annotationScanDebug) trace( 226 typeid(Z), 227 " is annotated with @callback annotation, supplying component factory with construction callable of ", 228 typeid(CallbackFactories[index].dg), 229 " and provided args ", 230 CallbackFactories[index].args 231 ); 232 instantiator.setInstanceFactory(callbackFactory(CallbackFactories[index].dg, CallbackFactories[index].args)); 233 } 234 } 235 } 236 } 237 238 /** 239 Set value factory that takes component from @value annotation and provides it as a new component. 240 **/ 241 @safe struct ValueFactoryConfiguratorPolicy { 242 243 /** 244 Set value factory that takes component from @value annotation and provides it as a new component. 245 246 Params: 247 instantiator = factory which is set value instance factory from @value annotation 248 locator = locator used by factory 249 **/ 250 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 251 alias ValueAnnotations = allUDAs!Z; 252 static foreach (index; 0 .. ValueAnnotations.length) { 253 static if (isValueAnnotation!(ValueAnnotations[index])) { 254 255 debug(annotationScanDebug) trace(typeid(Z), " is annotated with @value annotation, using ", ValueAnnotations[index].value, " as prebuilt component"); 256 instantiator.setInstanceFactory(new ValueInstanceFactory!Z(ValueAnnotations[index].value)); 257 } 258 } 259 } 260 } 261 262 /** 263 A policy that uses annotations that implement isConfigurerPolicy interface to configure the component factory 264 **/ 265 @safe struct GenericConfigurerConfiguratorPolicy { 266 /** 267 Scans for annotations implementing isConfigurerPolicy interface and uses them to configure component factory. 268 269 Params: 270 instantiator = factory upon which isConfigurerPolicy annotations are applied 271 locator = locator used by factory 272 **/ 273 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 274 alias GenericConfigurerConfigurators = allUDAs!Z; 275 static foreach (index; 0 .. GenericConfigurerConfigurators.length) { 276 static if (isConfiguratorPolicy!(toType!(GenericConfigurerConfigurators[index]), T)) { 277 278 debug(annotationScanDebug) trace( 279 typeid(Z), 280 " is annotated with configuration policy ", 281 typeid(GenericConfigurerConfigurators[index]), 282 " invoking configure method on it using ", 283 instantiator, 284 " and ", 285 locator 286 ); 287 GenericConfigurerConfigurators[index].configure!(T)(instantiator, locator); 288 } 289 } 290 } 291 } 292 293 /** 294 A policy that configures factory to use callback to destroy created components. 295 **/ 296 @safe struct CallbackDestructorConfigurerPolicy { 297 /** 298 Configure instantiator to use callback for destruction of components 299 300 Params: 301 instantiator = component factory 302 locator = container for component's dependencies 303 Z = type of component created 304 **/ 305 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 306 auto CallbackDestructors = allUDAs!Z; 307 foreach (index; 0 .. CallbackDestructors.length) { 308 static if (isCallbackDestructor!(CallbackDestructors[index], T)) { 309 310 debug(annotationsScanDebug) trace( 311 typeid(Z), " is annotated with @callbackDestructor callable of ", CallbackDestructors[index].dg, " with arguments of ", CallbackDestructors[index].args 312 ); 313 instantiator.setInstanceDestructor(callbackDestructor(CallbackDestructors[index].dg, CallbackDestructors[index].args)); 314 } 315 } 316 } 317 } 318 319 /** 320 A dummy structure that is providing a simple field for isFieldConfiguratorPolicy interface for testing purposes of configurator templates. 321 **/ 322 struct ConfiguredFieldTester { 323 324 /** 325 Dummy field 326 **/ 327 int field; 328 } 329 330 /** 331 A dummy structure that is providing a simple method for isMethodConfiguratorPolicy interface for testing purposes of configurator templates. 332 **/ 333 struct ConfiguredMethodTester { 334 335 /** 336 Dummy method 337 **/ 338 void method(int x) {} 339 } 340 341 /** 342 Check if T is a field configurator policy that operates upon fields of a component. 343 344 Params: 345 T = the type that is tested for field configurator policy interface 346 member = member field of constructed component by GenericFactory!Z 347 X = a factory for a component, on which policy can operate 348 Returns: 349 true if it is compliant, false otherwise 350 **/ 351 enum bool isFieldConfiguratorPolicy(T, string member = "field", X : GenericFactory!Z = GenericFactory!ConfiguredFieldTester, Z) = 352 is(T == struct) && 353 is(typeof(&T.configureField!(member, X)) : void function(X, Locator!())); 354 355 /** 356 Check if T is a field configurator policy that operates upon methods of a component. 357 358 Params: 359 T = the type that is tested for field configurator policy interface 360 member = member field of constructed component by GenericFactory!Z 361 X = a factory for a component, on which policy can operate 362 363 Returns: 364 true if it is compliant, false otherwise 365 **/ 366 enum bool isMethodConfiguratorPolicy(T, string member = "method", X : GenericFactory!Z = GenericFactory!ConfiguredMethodTester, Z) = 367 is(T == struct) && 368 is(typeof(&T.configureMethod!(member, X)) : void function(X, Locator!())); 369 370 /** 371 Configurator policy that applies method configurator policies on all public methods of a component 372 **/ 373 @safe struct MethodScanningConfiguratorPolicy(MethodConfiguratorPolicies...) 374 if (allSatisfy!(isMethodConfiguratorPolicy, MethodConfiguratorPolicies)) { 375 376 /** 377 Apply a set of method configurator policies on all public methods of component Z 378 379 Params: 380 Z = component which will have it's methods scanned, including overloads. 381 instantiator = Z component factory 382 locator = locator used by method configurator policies 383 **/ 384 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 385 foreach (member; __traits(allMembers, Z)) { 386 static if ((getProtection!(Z, member) == "public") && isSomeFunction!(__traits(getMember, Z, member))) { 387 foreach (methodConfigurer; MethodConfiguratorPolicies) { 388 methodConfigurer.configureMethod!member(instantiator, locator); 389 } 390 } 391 } 392 } 393 } 394 395 /** 396 Configurator policy that applies field configurator policies on all public methods of a component 397 **/ 398 @safe struct FieldScanningConfiguratorPolicy(FieldConfiguratorPolicies...) 399 if (allSatisfy!(isFieldConfiguratorPolicy, FieldConfiguratorPolicies)) { 400 401 /** 402 Apply a set of field configurator policies on all public fields of component Z 403 404 Params: 405 Z = component which will have it's methods scanned, including overloads. 406 instantiator = Z component factory 407 locator = locator used by method configurator policies 408 **/ 409 static void configure(T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 410 foreach (member; __traits(allMembers, Z)) { 411 static if ((getProtection!(Z, member) == "public") && isField!(Z, member)) { 412 foreach (fieldConfigurer; FieldConfiguratorPolicies) { 413 fieldConfigurer.configureField!member(instantiator, locator); 414 } 415 } 416 } 417 } 418 } 419 420 /** 421 Method configurator policy that scans only constructors for @constructor annotation, for using them to instantiate component Z 422 **/ 423 @safe struct ConstructorMethodConfiguratorPolicy { 424 425 /** 426 Checks if scanned method is a constructor and has @constructor annotation, and sets it to be used to construct component if so. 427 428 Params: 429 Z = component type 430 member = member that should be scanned, only constructors are taken into account 431 instantiator = Z component factory, which policy will set constructor to be used to construct component if possible 432 locator = locator used to supply dependencies into constructor of component 433 **/ 434 static void configureMethod(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 435 436 static if (member == "__ctor") { 437 static foreach (overload; __traits(getOverloads, Z, member)) {{ 438 439 440 alias Configurers = allUDAs!overload; 441 442 static foreach (index; 0 .. Configurers.length) {{ 443 static if (isConstructorAnnotation!(Configurers[index])) { 444 445 debug(annotationScanDebug) trace( 446 typeid(Z), " constructor", typeid(Parameters!overload), 447 " is annotated with @constructor annotation, using it as means to construct component using supplied arguments of ", Configurers[index].args 448 ); 449 auto tuple = Configurers[index].args; 450 instantiator.setInstanceFactory( 451 constructorBasedFactory!Z(tuple) 452 ); 453 } 454 }} 455 }} 456 } 457 } 458 } 459 460 /** 461 Method policy that scans constructors for @autowired annotation to use them to construct component with dependencies identified by qualifier, name or their type. 462 **/ 463 @safe struct AutowiredConstructorMethodConfiguratorPolicy { 464 465 /** 466 Configures instantiator to use @autowired constructor method, to construct the component. 467 468 Params: 469 Z = component type 470 member = method that is scanned. Only constructors are taken into account 471 instantiator = component constructor 472 locator = locator for component dependencies 473 **/ 474 static void configureMethod(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 475 476 static if (member == "__ctor") { 477 foreach (overload; __traits(getOverloads, Z, member)) {{ 478 479 alias Configurers = allUDAs!overload; 480 481 static foreach (index; 0 .. Configurers.length) { 482 static if (isAutowiredAnnotation!(Configurers[index])) { 483 auto references = makeFunctionParameterReferences!overload.expand; 484 debug(annotationScanDebug) trace( 485 typeid(Z), " constructor", typeid(Parameters!(overload)), 486 " is annotated with @autowired annotation, constructing component using this constructor with arguments of ", references 487 ); 488 489 instantiator.setInstanceFactory( 490 constructorBasedFactory!Z(references) 491 ); 492 } 493 } 494 }} 495 } 496 } 497 } 498 499 /** 500 Field configurator policy that will set a field annotated @setter annotation to value contained in it. 501 **/ 502 @safe struct SetterFieldConfiguratorPolicy { 503 504 /** 505 Configures instantiator to inject into field a predefined value or a dependency from @setter annotation 506 507 Params: 508 Z = component type 509 member = field member of component 510 instantiator = component factory 511 locator = locator for component dependencies 512 **/ 513 static void configureField(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 514 515 alias Configurers = allUDAs!(__traits(getMember, Z, member)); 516 517 static foreach (index; 0 .. Configurers.length) {{ 518 static if (isSetterAnnotation!(Configurers[index])) { 519 520 debug(annotationScanDebug) trace( 521 typeid(Z), ".", member, 522 " field is marked with @setter annotation. Using it to confiugre component with provided arguments of ", Configurers[index].args 523 ); 524 525 auto tuple = Configurers[index].args; 526 instantiator.addPropertyConfigurer(fieldConfigurer!(member, Z)(tuple)); 527 } 528 }} 529 } 530 } 531 532 /** 533 Field configurator policy that will set a field to value returned by a callback in @callback annotation 534 **/ 535 @safe struct CallbackFieldConfiguratorPolicy { 536 537 /** 538 Configure instantiator to inject return value of callback from @callback annotation into field 539 540 Params: 541 member = field that will be injected 542 Z = component type 543 instantiator = component factory 544 locator = locator for component dependencies 545 **/ 546 static void configureField(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 547 548 alias Callbacks = allUDAs!(__traits(getMember, Z, member)); 549 550 static foreach (index; 0 .. Callbacks.length) { 551 static if (isCallbackConfigurerAnnotation!(toType!(Callbacks[index]))) { 552 553 debug(annotationScanDebug) trace( 554 typeid(Z), ".", member, " field is annotated with @callback annotation, using callback", 555 Callbacks[index].dg, " with ", Callbacks[index].args, " to inject field with data." 556 ); 557 instantiator.addPropertyConfigurer(callbackConfigurer!Z(Callbacks[index].dg, Callbacks[index].args)); 558 } 559 } 560 } 561 } 562 563 /** 564 Field configurator policy that will try to inject a dependency that matches fields type for fields that are annotated with @autowired annotation 565 **/ 566 @safe struct AutowiredFieldConfiguratorPolicy { 567 568 /** 569 Configure instantiator to inject into field a component identified by field's type if field has @autowired annotation. 570 571 Params: 572 member = field that will be injected 573 Z = component type 574 instantiator = component factory 575 locator = locator for component dependencies 576 **/ 577 static void configureField(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 578 alias field = Alias!(__traits(getMember, Z, member)); 579 580 alias Callbacks = allUDAs!field; 581 582 static foreach (index; 0 .. Callbacks.length) {{ 583 static if (isAutowiredAnnotation!(Callbacks[index])) { 584 585 RuntimeReference reference; 586 587 mixin(transformToReference("reference", "field")); 588 589 debug(annotationScanDebug) trace(typeid(Z), ".", member, " field is annotated with @autowired annotation, injecting it with ", reference); 590 591 instantiator.addPropertyConfigurer(fieldConfigurer!(member, Z)(reference)); 592 } 593 }} 594 } 595 } 596 597 /** 598 Method configurator policy that will call a method with resolved arguments from @setter annotation. 599 **/ 600 @safe struct SetterMethodConfiguratorPolicy { 601 602 /** 603 Configure instantiator to call @setter annotated method with arguments from annotation 604 605 Params: 606 Z = component type 607 member = method that would be called by factory 608 instantiator = component factory 609 locator = locator for component dependencies 610 **/ 611 static void configureMethod(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 612 613 static foreach (overload; __traits(getOverloads, Z, member)) {{ 614 615 alias Configurers = allUDAs!overload; 616 617 static foreach (index; 0 .. Configurers.length) {{ 618 static if (isSetterAnnotation!(Configurers[index])) { 619 debug(annotationScanDebug) trace(typeid(Z), ".", member, " method is marked with @setter annotation, injecting it with ", Configurers[index].args); 620 auto tuple = Configurers[index].args; 621 instantiator.addPropertyConfigurer(methodConfigurer!(member, Z)(tuple)); 622 } 623 }} 624 }} 625 } 626 } 627 628 /** 629 Method configurator policy that will call callback from @callback annotated methods. 630 **/ 631 @safe struct CallbackMethodConfiguratorPolicy { 632 633 /** 634 Configre instantiator to call callback from @callback annotation with arguments that are stored in annotation 635 636 Params: 637 Z = component type 638 member = method which is annotated with @callback. It is supposed that callback will use it somehow in logic. 639 instantiator = component factory 640 locator = locator for component dependencies 641 **/ 642 static void configureMethod(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 643 644 static foreach (overload; __traits(getOverloads, Z, member)) {{ 645 646 alias Configurers = allUDAs!overload; 647 648 static foreach (index; 0 .. Configurers.length) { 649 static if (isCallbackConfigurerAnnotation!(toType!(Configurers[index]))) { 650 651 debug(annotationScanDebug) trace(typeid(Z), ".", member, " method is marked with @callback annotation, using callback", Configurers[index].dg, " with ", Configurers[index].args, " to configure component."); 652 instantiator.addPropertyConfigurer(callbackConfigurer!Z(Configurers[index].dg, Configurers[index].args)); 653 } 654 } 655 }} 656 } 657 } 658 659 /** 660 Method configurator policy that will call method annotated with @autowire with arguments extracted from locator identified by qualifier, name or their type. 661 **/ 662 @safe struct AutowiredMethodConfiguratorPolicy { 663 664 /** 665 Configure instantiator to call a method annotated with @autowired with arguments extracted from locator 666 667 Params: 668 Z = component type 669 member = method that is annotated with @autowired annotation 670 instantiator = component factory 671 locator = locator for component dependencies 672 **/ 673 static void configureMethod(string member, T : GenericFactory!Z, Z)(T instantiator, Locator!() locator) { 674 static if (member != "__ctor") { 675 static foreach (overload; __traits(getOverloads, Z, member)) {{ 676 677 alias Configurers = allUDAs!overload; 678 679 static foreach (index; 0 .. Configurers.length) { 680 static if (isAutowiredAnnotation!(Configurers[index])) { 681 auto references = makeFunctionParameterReferences!overload.expand; 682 683 debug(annotationScanDebug) trace(typeid(Z), ".", member, " method is marked with @autowired annotation, injecting it with ", references); 684 685 instantiator.addPropertyConfigurer(methodConfigurer!(member, Z)(references)); 686 } 687 } 688 }} 689 } 690 } 691 } 692 693 /** 694 Check if a policy implements transformer interface. 695 696 A transformer is a policy that takes a type, and optionally a member of it, and transform it 697 into a component factory using annotations from component, or member. 698 699 Params: 700 T = type to be tested for interface implementation 701 X = a test object that is used to test templated methods of policy 702 Returns: 703 true if it implements Transformer interface, false otherwise 704 **/ 705 enum bool isTransformer(T, X = Object) = is(T == struct) && is(typeof(&T.transform!X) : void function (Locator!(), Storage!(ObjectFactory, string))); 706 707 /** 708 A transformer that creates out of a type a GenericFactory for passed type 709 **/ 710 @safe struct TypeTransformer(FactoryPolicy, ConfigurerPolicy) 711 if (isFactoryPolicy!FactoryPolicy && isConfiguratorPolicy!ConfigurerPolicy) { 712 713 /** 714 Transform type T into a GenericFactory!T 715 716 Params: 717 T = type which is transformed into it's factory 718 locator = locator for T's dependencies 719 Returns: 720 instance of GenericFactory!T, or null if T is not transformable into a factory 721 **/ 722 static auto transform(T)(Locator!() locator) 723 if (is(T)) { 724 auto instantiator = FactoryPolicy.createFactory!T(locator); 725 726 if (instantiator is null) { 727 return instantiator; 728 } 729 730 ConfigurerPolicy.configure(instantiator, locator); 731 732 return instantiator; 733 } 734 } 735 736 /** 737 Transformer that wraps results of another transformer in WrappingFactory 738 **/ 739 @safe struct ObjectFactoryTransformer(TransformerPolicy) { 740 741 /** 742 Wrap up results of another transformer into WrappingFactory 743 744 Params: 745 T = type of component that is transformed 746 747 Returns: 748 null if T is not transformable, or instance of WrappingFactory that wraps result of TransformerPolicy 749 **/ 750 static auto transform(T)(Locator!() locator) 751 if (is(T)) { 752 753 auto instantiator = TransformerPolicy.transform!T(locator); 754 755 if (instantiator is null) { 756 return cast(WrappingFactory!(typeof(instantiator))) null; 757 } 758 759 return new WrappingFactory!(typeof(instantiator))(instantiator); 760 } 761 } 762 763 /** 764 Check if T implements ContainerAdder interface. 765 766 A ContainerAdder is component that is responsible to scan 767 a symbol for it's members, transform them using passed Transformers, 768 and add them to storage. 769 770 Params: 771 T = component that is tested for ContainerAdder interface. 772 X = a dummy symbol used to test templated methods of component 773 774 Note: 775 Due to possibility to store any kind of symbol in X (not only types), 776 the use of this checker is limited in use for generic testing. 777 Ex. ContainerAdder for modules and types are technically different interfaces 778 and will fail the checker when are tested both with same X argument 779 (module is not a type), though they are conceptually the same. 780 Returns: 781 true if it implements ContainerAdder interface, false otherwise 782 **/ 783 enum bool isContainerAdder(T, alias X = Object) = 784 is(T == struct) && 785 is(typeof(&T.scan!X) : V function (X, Y), X : Locator!(), Y : Storage!(Factory!Object, string), V); 786 787 /** 788 Check if T implements component storing interface. 789 790 The responsibility of component storing policy is to 791 store component's factory into appropiate storage, 792 by apropiate identity, using information about type T. 793 794 Params: 795 T = type that is tested for interface compliance 796 X = component type against which T's templates are tested to comply to interface 797 Returns: 798 true if it implements the interface, false otherwise 799 **/ 800 enum bool isComponentStoringPolicy(T, X = Object) = 801 is(T == struct) && 802 is( 803 typeof(&T.store!X) : ComponentStoringResult function (F, Locator!(), S), 804 F : Factory!Z, 805 Z, 806 S : Storage!(Factory!Z, string) 807 ); 808 809 /** 810 Check if T implements identity resolver interface. 811 812 The responsibility of identity resolver policy is to 813 find an identity for component that is to be stored based 814 on information provided on passed type. 815 816 Params: 817 T = type that is tested for interface compliance 818 X = component type against which T's templates are tested to comply to interface 819 Returns: 820 true if it implements the interface, false otherwise 821 **/ 822 enum bool isIdentityResolverPolicy(T, X = Object) = 823 is(T == struct) && 824 is( 825 typeof(&T.resolve!X) : string function (F), 826 F : Factory!Z, 827 Z 828 ); 829 830 /** 831 Check if T implements storage resolver interface. 832 833 The responsibility of storage resolver policy is to 834 find a storage for component which will store component 835 based on information provided on passed type. 836 837 Params: 838 T = type that is tested for interface compliance 839 X = component type against which T's templates are tested to comply to interface 840 Returns: 841 true if it implements the interface, false otherwise 842 **/ 843 enum bool isStorageLocatorPolicy(T, X = Object) = 844 is(T == struct) && 845 is( 846 typeof(&T.search!X) : Storage!(Factory!Z, string) function (F, Locator!(), S), 847 F : Factory!Z, 848 Z, 849 S : Storage!(Factory!Z, string) 850 ); 851 852 /** 853 Check if T implements identity aliasing interface. 854 855 The responsibility of identity aliasing policy is to 856 find aliases of component's identity that should be registered 857 in component storage. 858 859 Params: 860 T = type that is tested for interface compliance 861 X = component type against which T's templates are tested to comply to interface 862 Returns: 863 true if it implements the interface, false otherwise 864 **/ 865 enum bool isAliasingPolicy(T, X = Object) = 866 is(T == struct) && 867 is( 868 typeof(&T.link!X) : void function (string, AliasAware!string) 869 ); 870 871 /** 872 Result of storing a component into a storage. 873 **/ 874 struct ComponentStoringResult { 875 876 /** 877 Primary identity of stored component 878 **/ 879 string identity; 880 881 /** 882 Storage where it was stored. 883 **/ 884 Storage!(ObjectFactory, string) storage; 885 } 886 887 /** 888 A default implementation of component storing policy that looks for @qualifier and @contained annotations to store component factory. 889 **/ 890 @safe struct ComponentStoringPolicy(IdentityResolverPolicy, StorageResolvingPolicy, AliasingPolicyImpl) { 891 /** 892 Store component factory into storage. 893 894 Store component factory into storage. The storage where component is stored 895 on presence of @contained annotation on T, will be extracted from locator, otherwise 896 storage passed as argument is used. The identity of component by default is 897 it's type FQN, but is overriden by @qualifier annotation. 898 899 Params: 900 factory = component factory to be stored 901 locator = locator that is used to fetch storage in case of @contained annotation 902 storage = storage were component is stored when no @contained annotation is provided 903 **/ 904 static ComponentStoringResult store(alias T)(Factory!Object factory, Locator!() locator, Storage!(Factory!Object, string) storage) { 905 906 string identity = IdentityResolverPolicy.resolve!T(factory); 907 908 auto destinationStorage = StorageResolvingPolicy.search!T(factory, locator, storage); 909 910 if ((identity !is null) && (destinationStorage !is null)) { 911 destinationStorage.set(factory, identity); 912 913 AliasAware!string aliasingContainer = (() @trusted => cast(AliasAware!string) destinationStorage)(); 914 if (aliasingContainer !is null) { 915 AliasingPolicyImpl.link!T(identity, aliasingContainer); 916 } 917 918 return ComponentStoringResult(identity, destinationStorage); 919 } 920 921 922 return ComponentStoringResult.init; 923 } 924 } 925 926 /** 927 A implementation that will chain several storing policies, triggerring multiple registrations of same component. 928 **/ 929 @safe struct ChainedComponentStoringPolicy(ComponentStoringPolicies...) 930 if (allSatisfy!(isComponentStoringPolicy, ComponentStoringPolicies)) { 931 932 /** 933 Store component factory into multiple storages by using multiple policies. 934 935 Params: 936 factory = component factory to be stored 937 locator = locator that is used to fetch storage in case of @contained annotation 938 storage = storage were component is stored when no @contained annotation is provided 939 **/ 940 static ComponentStoringResult store(alias T)(Factory!Object factory, Locator!() locator, Storage!(Factory!Object, string) storage) { 941 ComponentStoringResult result; 942 foreach (ComponentStoringPolicy; ComponentStoringPolicies) { 943 result = ComponentStoringPolicy.store!T(factory, locator, storage); 944 945 if (result !is ComponentStoringResult.init) { 946 break; 947 } 948 } 949 950 return result; 951 } 952 } 953 954 /** 955 Default implementation of storage policy used by adders. 956 **/ 957 alias ComponentStoringPolicyImpl = ComponentStoringPolicy!( 958 IdentityResolverPolicyImpl, 959 StorageLocatorPolicyImpl, 960 AliasingPolicyImpl 961 ); 962 963 /** 964 Qualifier implementation of aliasing policy. 965 **/ 966 @safe struct QualifiedAnnotationAliasingPolicy { 967 968 /** 969 Alias an identity in a aliasing container based on qualifier annotations on component. 970 971 Params: 972 identity = main identity of component registered in aliased container. 973 aliasingContainer = container that is keeping aliasing information. 974 **/ 975 static void link(alias T)(string identity, AliasAware!string aliasingContainer) { 976 977 alias Qualifiers = allUDAs!T; 978 static foreach (index; 0 .. Qualifiers.length) { 979 static if (isQualifierAnnotation!(Qualifiers[index])) { 980 if (identity != Qualifiers[index].id) { 981 debug(annotationScanDebug) trace( 982 fullyQualifiedName!T, " is marked with @qualifier annotation, aliasing identitity ", 983 identity, " of component to ", Qualifiers[index].id 984 ); 985 986 aliasingContainer.link(identity, Qualifiers[index].id); 987 } 988 } 989 } 990 } 991 } 992 993 /** 994 By identifier implementation of aliasing policy. 995 **/ 996 @safe struct IdentifierAliasingPolicy { 997 998 /** 999 Alias an identity in a aliasing container based on it's identifier. 1000 1001 Params: 1002 identity = main identity of component registered in aliased container. 1003 aliasingContainer = container that is keeping aliasing information. 1004 **/ 1005 static void link(alias T)(string identity, AliasAware!string aliasingContainer) { 1006 1007 static if (is(typeof(__traits(identifier, T)))) { 1008 static immutable identifier = __traits(identifier, T); 1009 if (identifier != identity) { 1010 debug(annotationScanDebug) trace( 1011 fullyQualifiedName!T, " has identifier of ", identifier, ", aliasing it to identitity of ", identity 1012 ); 1013 1014 aliasingContainer.link(identity, identifier[]); 1015 } 1016 } 1017 } 1018 } 1019 1020 /** 1021 By type implementation of aliasing policy. 1022 **/ 1023 @safe struct TypeAliasingPolicy { 1024 1025 /** 1026 Alias an identity in a aliasing container based on type of component. 1027 1028 Params: 1029 identity = main identity of component registered in aliased container. 1030 aliasingContainer = container that is keeping aliasing information. 1031 **/ 1032 static void link(alias T)(string identity, AliasAware!string aliasingContainer) { 1033 1034 static if (is(typeof({ auto test = typeid(T); }))) { 1035 1036 if (typeid(T).toString != identity) { 1037 debug(annotationScanDebug) trace( 1038 typeid(T), " has identity ", identity, " which is not FQN of component, aliasing it to ", typeid(T), " of component using typeid" 1039 ); 1040 aliasingContainer.link(identity, typeid(T).toString); 1041 } 1042 1043 if ((typeid(T).toString != fullyQualifiedName!T) && (fullyQualifiedName!T != identity)) { 1044 debug(annotationScanDebug) trace( 1045 typeid(T), " has identity ", identity, " which is not FQN of component, aliasing it to ", typeid(T), " of component using fullyQualifiedName" 1046 ); 1047 aliasingContainer.link(identity, fullyQualifiedName!T); 1048 } 1049 } 1050 } 1051 } 1052 1053 /** 1054 Chaining implementation of aliasing policy, calling other passed policies. 1055 **/ 1056 @safe struct ChainedAliasingPolicy(Policies...) 1057 if (allSatisfy!(isAliasingPolicy, Policies)) { 1058 1059 /** 1060 Alias an identity in a aliasing container using aliasing policies passed to this policy. 1061 1062 Params: 1063 identity = main identity of component registered in aliased container. 1064 aliasingContainer = container that is keeping aliasing information. 1065 **/ 1066 static void link(alias T)(string identity, AliasAware!string aliasingContainer) { 1067 static foreach (Policy; Policies) { 1068 Policy.link!T(identity, aliasingContainer); 1069 } 1070 } 1071 } 1072 1073 /** 1074 Default configuration of aliasing policy 1075 **/ 1076 alias AliasingPolicyImpl = ChainedAliasingPolicy!( 1077 QualifiedAnnotationAliasingPolicy, 1078 IdentifierAliasingPolicy, 1079 TypeAliasingPolicy 1080 ); 1081 1082 /** 1083 A implementation storage locator policy that searches for storage of component based on @contained annotation 1084 **/ 1085 @safe struct ContainedAnnotationStorageLocatorPolicy { 1086 1087 /** 1088 Find a storge in locator based upon @contained annotation 1089 1090 Params: 1091 factory = component factory to be stored 1092 locator = locator that is used to fetch storage in case of @contained annotation is present 1093 storage = default storage 1094 **/ 1095 static Storage!(Factory!Object, string) search(alias T)(Factory!Object factory, Locator!() locator, Storage!(Factory!Object, string) storage) { 1096 string id; 1097 1098 alias ContainedAnnotations = allUDAs!T; 1099 static foreach (index; 0 .. ContainedAnnotations.length) { 1100 static if (isContainedAnnotation!(ContainedAnnotations[index])) { 1101 1102 if (locator.has(ContainedAnnotations[index].id)) { 1103 debug(annotationScanDebug) trace( 1104 typeid(T), " is marked with custom @contained annotation, using storage identified by ", 1105 ContainedAnnotations[index].id, " to store component factory" 1106 ); 1107 id = ContainedAnnotations[index].id; 1108 } 1109 } 1110 } 1111 1112 if (id !is null) { 1113 return locator.locate!(Storage!(Factory!Object, string))(id); 1114 } 1115 1116 return null; 1117 } 1118 } 1119 1120 /** 1121 A implementation storage locator policy that will return default storage for component which is passed as argument to search. 1122 **/ 1123 @safe struct DefaultStorageLocatorPolicy { 1124 1125 /** 1126 Find a storge in locator based upon @contained annotation 1127 1128 Params: 1129 factory = component factory to be stored 1130 locator = locator that is used to fetch storage in case of @contained annotation 1131 storage = default storage 1132 **/ 1133 static Storage!(Factory!Object, string) search(alias T)(Factory!Object factory, Locator!() locator, Storage!(Factory!Object, string) storage) { 1134 1135 return storage; 1136 } 1137 } 1138 1139 /** 1140 A implementation storage locator policy that will apply other policies in chain until one provides a storage for storing data. 1141 **/ 1142 @safe struct ChainedStorageLocatorPolicy(StorageLocatorPolicies...) 1143 if (allSatisfy!(isStorageLocatorPolicy, StorageLocatorPolicies)) { 1144 1145 /** 1146 Get default storage for component. 1147 1148 Params: 1149 factory = component factory to be stored 1150 locator = locator that is used to fetch storage in case of @contained annotation 1151 storage = default storage 1152 **/ 1153 static Storage!(Factory!Object, string) search(alias T)(Factory!Object factory, Locator!() locator, Storage!(Factory!Object, string) storage) { 1154 1155 static foreach (StorageLocatorPolicy; StorageLocatorPolicies) {{ 1156 auto destination = StorageLocatorPolicy.search!T(factory, locator, storage); 1157 1158 if (destination !is null) { 1159 return destination; 1160 } 1161 }} 1162 1163 return null; 1164 } 1165 } 1166 1167 /** 1168 Default policy implementation for searching of storage for component 1169 **/ 1170 alias StorageLocatorPolicyImpl = ChainedStorageLocatorPolicy!( 1171 ContainedAnnotationStorageLocatorPolicy, 1172 DefaultStorageLocatorPolicy 1173 ); 1174 1175 /** 1176 A policy for resolving identity of component by annotation. 1177 **/ 1178 @safe struct QualifiedAnnotationIdentityResolverPolicy { 1179 1180 /** 1181 Resolve identity of component by qualified annotations on it. 1182 1183 The first qualified annotation is returned from list of annotations. If no annotations 1184 are on the component T, null is returned. 1185 1186 Params: 1187 factory = component factory to be stored 1188 1189 Returns: 1190 identity of component based on annotation or null. 1191 **/ 1192 static string resolve(alias T)(Factory!Object factory) { 1193 string identity; 1194 1195 alias Qualifiers = allUDAs!T; 1196 static foreach (index; 0 .. Qualifiers.length) {{ 1197 static if (!is(typeof(found)) && isQualifierAnnotation!(Qualifiers[index])) { 1198 debug(annotationScanDebug) trace( 1199 fullyQualifiedName!T, " is marked with @qualifier annotation, using ", Qualifiers[index].id, " as main identity for component." 1200 ); 1201 identity = Qualifiers[index].id; 1202 enum found = true; 1203 } 1204 }} 1205 1206 return identity; 1207 } 1208 } 1209 1210 /** 1211 A policy for resolving identity of component by it's type. 1212 **/ 1213 @safe struct TypeIdentityResolverPolicy { 1214 1215 /** 1216 Resolve identity of component by FQN of it's type. 1217 1218 Params: 1219 factory = component factory to be stored 1220 1221 Returns: 1222 FQN of component type as it's identity. 1223 **/ 1224 static string resolve(alias T)(Factory!Object factory) { 1225 1226 debug(annotationScanDebug) trace( 1227 fullyQualifiedName!T, " has no custom identities, using it's type FQN as main identity." 1228 ); 1229 return fullyQualifiedName!T; 1230 } 1231 } 1232 1233 /** 1234 A policy for resolving identity of component by it's identifier. 1235 **/ 1236 @safe struct IdentifierBasedIdentityResolverPolicy { 1237 1238 /** 1239 Resolve identity of component by identifier attached to it. 1240 1241 Params: 1242 factory = component factory to be stored 1243 1244 Returns: 1245 identifier as identity of component or null. 1246 **/ 1247 static string resolve(alias T)(Factory!Object factory) { 1248 1249 static if (is(typeof(__traits(identifier, T)))) { 1250 static immutable string identifier = __traits(identifier, T); 1251 debug(annotationScanDebug) trace( 1252 fullyQualifiedName!T, " has identifier ", identifier, " using it as main identity for component." 1253 ); 1254 1255 return identifier; 1256 } else { 1257 1258 return null; 1259 } 1260 } 1261 } 1262 1263 /** 1264 A policy for resolving identity of component by applying other components in chain. 1265 **/ 1266 @safe struct ChainedIdentityResolverPolicy(IdentityResolverPolicies...) 1267 if (allSatisfy!(isIdentityResolverPolicy, IdentityResolverPolicies)) { 1268 1269 /** 1270 Resolve identity of component by FQN of it's type. 1271 1272 Params: 1273 factory = component factory to be stored 1274 1275 Returns: 1276 FQN of component type as it's identity. 1277 **/ 1278 static string resolve(alias T)(Factory!Object factory) { 1279 1280 static foreach (IdentityResolverPolicy; IdentityResolverPolicies) {{ 1281 string identity = IdentityResolverPolicy.resolve!T(factory); 1282 1283 if (identity !is null) { 1284 return identity; 1285 } 1286 }} 1287 1288 return null; 1289 } 1290 } 1291 1292 /** 1293 Default implementation of identity resolving policy 1294 **/ 1295 alias IdentityResolverPolicyImpl = ChainedIdentityResolverPolicy!( 1296 QualifiedAnnotationIdentityResolverPolicy, 1297 TypeIdentityResolverPolicy 1298 ); 1299 1300 /** 1301 ContainerAdder that chains a set of ContainerAdders on a symbol. 1302 **/ 1303 @safe struct ChainedContainerAdder(ContainerAdderPolicies...) { 1304 import aermicioi.aedi.util.traits : getMember; 1305 1306 /** 1307 Check if at least one ContainerAdder supports passed symbol 1308 1309 Params: 1310 T = symbol that at least one ContainerAdder, must know how to scan 1311 Returns: 1312 true if at least one ContainerAdder knows how to scan symbol, false otherwise 1313 **/ 1314 enum bool isSupported(alias T) = anySatisfied!(T, staticMap!(ApplyRight!(getMember, "isSupported"), ContainerAdderPolicies)); 1315 1316 /** 1317 Apply all ContainerAdders that know how to scan T symbol 1318 1319 Params: 1320 T = symbol to be scanned 1321 locator = locator of components, used by transformer that is applied on members of scanned T symbol 1322 storage = the storage that will store component factories from transformed members of T symbol 1323 **/ 1324 static void scan(alias T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1325 if (isSupported!T) { 1326 foreach (ContainerAdderPolicy; ContainerAdderPolicies) { 1327 static if (ContainerAdderPolicy.isSupported!T) { 1328 ContainerAdderPolicy.scan!T(locator, storage); 1329 } 1330 } 1331 } 1332 } 1333 1334 /** 1335 Applies a transformer on passed symbol if it is a type. 1336 **/ 1337 @safe struct TypeContainerAdder(TypeTransformerPolicy, ComponentStoringPolicy = ComponentStoringPolicyImpl) 1338 if (isComponentStoringPolicy!ComponentStoringPolicy) { 1339 1340 /** 1341 Check if symbol T is a type definition. 1342 1343 Params: 1344 T = symbol to be tested 1345 Returns: 1346 true if it is a type, false otherwise 1347 **/ 1348 enum bool isSupported(alias T) = is(T); 1349 1350 /** 1351 Transform T component into a factory using TypeTransformerPolicy 1352 1353 Params: 1354 T = component type 1355 locator = component locator used by transformed component factory 1356 storage = storage wich will contain component factory transformed out of T 1357 **/ 1358 static void scan(T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1359 if (isSupported!T) { 1360 1361 auto transformed = TypeTransformerPolicy.transform!T(locator); 1362 1363 if (transformed is null) { 1364 return; 1365 } 1366 1367 ComponentStoringPolicy.store!T(transformed, locator, storage); 1368 } 1369 } 1370 1371 /** 1372 ContainerAdder that scans a type for inner static types, to transform and store into a storage. 1373 **/ 1374 @safe struct InnerTypeContainerAdder(ContainerAdderPolicy) { 1375 1376 /** 1377 Check if T symbol is a type 1378 1379 Params: 1380 T = symbol to be tested 1381 1382 Returns: 1383 true if it is a type, false otherwise 1384 **/ 1385 enum bool isSupported(alias T) = is(T); 1386 1387 /** 1388 Scan type T for inner static types, to transform them and store in storage 1389 1390 Params: 1391 T = type that will be scanned for inner static types 1392 locator = locator of components used by transformed component factories 1393 storage = storage which will contain transformed component factories 1394 **/ 1395 static void scan(T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1396 if(isSupported!T) { 1397 1398 foreach (member; __traits(allMembers, T)) { 1399 static if (isPublic!(T, member) && is(Alias!(__traits(getMember, T, member)))) { 1400 1401 ContainerAdderPolicy.scan!(getMember!(T, member))(locator, storage); 1402 scan!(getMember!(T, member))(locator, storage); 1403 } 1404 } 1405 } 1406 } 1407 1408 /** 1409 ContainerAdder that will scan a module for it's members, to transform into component factories and add them into a storage 1410 **/ 1411 @safe struct ModuleContainerAdder(ContainerAdderPolicy) { 1412 import std.algorithm : startsWith; 1413 /** 1414 Check if T symbol is a module. 1415 1416 Params: 1417 T = symbol to be tested 1418 1419 Returns: 1420 true if it is a module, false otherwise 1421 **/ 1422 enum bool isSupported(alias T) = T.stringof.startsWith("module"); 1423 1424 /** 1425 Scan module T, transform it's members into component factories, and store them into storage. 1426 1427 Params: 1428 T = module that is scanned 1429 locator = locator of components used by transformed component factories 1430 storage = storage which will contain component factories 1431 **/ 1432 static void scan(alias T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1433 if (isSupported!T) { 1434 1435 foreach (member; __traits(allMembers, T)) { 1436 static if (isPublic!(T, member) && is(Alias!(__traits(getMember, T, member)))) { 1437 ContainerAdderPolicy.scan!(getMember!(T, member))(locator, storage); 1438 } 1439 } 1440 } 1441 } 1442 1443 /** 1444 ContainerAdder that will ignore specific packages. 1445 1446 Params: 1447 pack = package that will be ignored. 1448 ContainerAdderPolicy = policy run on not ignored packages. 1449 **/ 1450 @safe struct IgnoringContainerAdder(string pack, ContainerAdderPolicy) { 1451 import std.algorithm : startsWith, canFind; 1452 import std.traits : packageName; 1453 /** 1454 Check if T symbol is a module. 1455 1456 Params: 1457 T = symbol to be tested 1458 1459 Returns: 1460 true if it is a module, false otherwise 1461 **/ 1462 enum bool isSupported(alias T) = (!moduleName!T.canFind(".") || !moduleName!T.startsWith(pack)) && ContainerAdderPolicy.isSupported!T; 1463 1464 /** 1465 Scan T if it is not in ignored package. 1466 1467 Params: 1468 T = module that is scanned 1469 locator = locator of components used by transformed component factories 1470 storage = storage which will contain component factories 1471 **/ 1472 static void scan(alias T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1473 if (isSupported!T) { 1474 ContainerAdderPolicy.scan!T(locator, storage); 1475 } 1476 } 1477 1478 /** 1479 ContainerAdder that will scan a type for it's methods, and use them to create component factories out of their return type 1480 **/ 1481 @safe struct FactoryMethodContainerAdder( 1482 ByTypeComponentStoringPolicy = ComponentStoringPolicyImpl, 1483 ByMethodComponentStoringPolicy = ComponentStoringPolicy!( 1484 ChainedIdentityResolverPolicy!( 1485 QualifiedAnnotationIdentityResolverPolicy, 1486 IdentifierBasedIdentityResolverPolicy 1487 ), 1488 StorageLocatorPolicyImpl, 1489 AliasingPolicyImpl 1490 ), 1491 ByTypeAliasingPolicy = TypeAliasingPolicy 1492 ) if (isComponentStoringPolicy!ByTypeComponentStoringPolicy && isComponentStoringPolicy!ByMethodComponentStoringPolicy && isAliasingPolicy!ByTypeAliasingPolicy) { 1493 import std.algorithm : startsWith; 1494 1495 /** 1496 Check if symbol T is a type or module 1497 1498 Params: 1499 T = symbol to be tested 1500 1501 Returns: 1502 true if it is a type, false otherwise 1503 **/ 1504 enum bool isSupported(alias T) = isModule!T || isType!T; 1505 1506 /** 1507 ditto 1508 **/ 1509 enum bool isModule(alias T) = T.stringof.startsWith("module"); 1510 1511 /** 1512 ditto 1513 **/ 1514 enum bool isType(alias T) = is(T); 1515 1516 /** 1517 Scan T's methods, for methods annotated with @component annotation, transform them into component factories 1518 that will use them to create components of returned type. 1519 1520 Params: 1521 T = type to be scanned 1522 locator = locator of components used by component factories 1523 storage = storage which will contain component factories 1524 **/ 1525 static void scan(alias T)(Locator!() locator, Storage!(ObjectFactory, string) storage) 1526 if (isSupported!T) { 1527 1528 static foreach (member; __traits(allMembers, T)) {{ 1529 static if (isPublic!(T, member) && isSomeFunction!(__traits(getMember, T, member))) { 1530 1531 static foreach (overload; __traits(getOverloads, T, member)) {{ 1532 1533 alias FactoryMethods = allUDAs!overload; 1534 1535 static foreach (index; 0 .. FactoryMethods.length) {{ 1536 static if (isComponentAnnotation!(FactoryMethods[index])) { 1537 auto factory = new WrappingFactory!(GenericFactoryImpl!(ReturnType!overload))( 1538 new GenericFactoryImpl!(ReturnType!overload)(locator) 1539 ); 1540 1541 if (factory !is null) { 1542 auto params = makeFunctionParameterReferences!overload.expand; 1543 1544 debug(annotationScanDebug) trace(fullyQualifiedName!T, ".", member, " is annotated with @component annotation, using it as constructor for component with args ", params); 1545 static if (isModule!T) { 1546 auto instanceFactory = functionInstanceFactory(&overload, params); 1547 } else static if (__traits(isStaticFunction, overload)) { 1548 auto instanceFactory = factoryMethodBasedFactory!(T, member)(params); 1549 } else { 1550 auto instanceFactory = factoryMethodBasedFactory!(T, member)(lref!T, params); 1551 } 1552 1553 import aermicioi.aedi.storage.decorator : Decorator; 1554 1555 static if (is(typeof(factory) : Decorator!X, X : GenericFactory!Z, Z)) { 1556 factory.decorated.setInstanceFactory = instanceFactory; 1557 } else static if (is(typeof(factory) : GenericFactory!Z, Z) && !is(Z == Object)) { 1558 factory.setInstanceFactory = instanceFactory; 1559 } 1560 1561 ComponentStoringResult result; 1562 result = ByMethodComponentStoringPolicy.store!overload(factory, locator, storage); 1563 1564 if (result !is ComponentStoringResult.init) { 1565 AliasAware!string container = (delegate AliasAware!string () @trusted => cast(AliasAware!string) result.storage)(); 1566 1567 if (container !is null) { 1568 ByTypeAliasingPolicy.link!(ReturnType!overload)(result.identity, container); 1569 } 1570 } else { 1571 1572 ByTypeComponentStoringPolicy.store!(ReturnType!overload)(factory, locator, storage); 1573 } 1574 } 1575 } 1576 }} 1577 }} 1578 } 1579 }} 1580 } 1581 } 1582 1583 /** 1584 Implementation of field scanning configurator policy, with built in field configurators. 1585 **/ 1586 alias FieldScanningConfiguratorPolicyImpl = FieldScanningConfiguratorPolicy!( 1587 SetterFieldConfiguratorPolicy, 1588 CallbackFieldConfiguratorPolicy, 1589 AutowiredFieldConfiguratorPolicy 1590 ); 1591 1592 /** 1593 Implementation of method scanning configurator policy, with built in method configurators. 1594 **/ 1595 alias MethodScanningConfiguratorPolicyImpl = MethodScanningConfiguratorPolicy!( 1596 ConstructorMethodConfiguratorPolicy, 1597 AutowiredConstructorMethodConfiguratorPolicy, 1598 SetterMethodConfiguratorPolicy, 1599 CallbackMethodConfiguratorPolicy, 1600 AutowiredMethodConfiguratorPolicy 1601 ); 1602 1603 /** 1604 Implementation of configurator policy, with built in configurators 1605 **/ 1606 alias ConfiguratorPolicyImpl = ChainedConfiguratorPolicy!( 1607 FieldScanningConfiguratorPolicyImpl, 1608 MethodScanningConfiguratorPolicyImpl, 1609 AllocatorConfiguratorPolicy, 1610 CallbackFactoryConfiguratorPolicy, 1611 ValueFactoryConfiguratorPolicy, 1612 GenericConfigurerConfiguratorPolicy 1613 ); 1614 1615 /** 1616 Implementation of factory policy, with built in factory creators 1617 **/ 1618 alias FactoryPolicyImpl = FallbackFactoryPolicy!( 1619 GenericFactoryPolicy, 1620 GenericFactoryAnnotationPolicy 1621 ); 1622 1623 /** 1624 Implementation of type to type factory transformer, with built in funcionality 1625 **/ 1626 alias TypeTransformerImpl = TypeTransformer!( 1627 FactoryPolicyImpl, 1628 ConfiguratorPolicyImpl 1629 ); 1630 1631 /** 1632 Implementation of object wrapping factory wrapping TypeTransformerImpl 1633 **/ 1634 alias ObjectFactoryTransformerImpl = 1635 ObjectFactoryTransformer!( 1636 TypeTransformerImpl 1637 ); 1638 1639 /** 1640 Implementation of module container adder, featuring built in scanners 1641 **/ 1642 alias ModuleContainerAdderImpl(TransformerPolicy = ObjectFactoryTransformerImpl) = ModuleContainerAdder!( 1643 IgnoringContainerAdder!("std", 1644 ChainedContainerAdder!( 1645 IgnoringContainerAdder!("std", TypeContainerAdder!TransformerPolicy), 1646 IgnoringContainerAdder!("std", InnerTypeContainerAdder!(TypeContainerAdder!TransformerPolicy)), 1647 IgnoringContainerAdder!("std", FactoryMethodContainerAdder!()), 1648 ) 1649 ) 1650 ); 1651 1652 /** 1653 Customizable implementation of container adder, with built in functionality 1654 **/ 1655 alias ContainerAdderImpl(TransformerPolicy = ObjectFactoryTransformerImpl) = ChainedContainerAdder!( 1656 IgnoringContainerAdder!("std", TypeContainerAdder!TransformerPolicy), 1657 IgnoringContainerAdder!("std", InnerTypeContainerAdder!(TypeContainerAdder!TransformerPolicy)), 1658 IgnoringContainerAdder!("std", FactoryMethodContainerAdder!()), 1659 ModuleContainerAdderImpl!TransformerPolicy 1660 ); 1661 1662 /** 1663 Template for defining scanning functions instantiated with particular container adder policy. 1664 1665 The functions defined in this template mixin are the entry point for running scans over symbols that 1666 are desired to be added into a container. The template will instantiate $(D_INLINECODE scan) family of functions 1667 that will use passed container adder policy to scan symbols passed to them. It is advised to define your own 1668 set of scanning methods in case when additional scanning and transformation logic is expected. 1669 **/ 1670 @safe mixin template Scanner(ContainerAdderPolicy) { 1671 1672 /** 1673 Scan symbol T for possible components using ContainerAdderPolicy 1674 1675 Params: 1676 storage = storage that will contain component factories 1677 locator = locator of components used to by component factories 1678 **/ 1679 void scan(alias T)(Storage!(ObjectFactory, string) storage, Locator!() locator) { 1680 debug(annotationScanDebug) trace(fullyQualifiedName!T, " will be scanned for possible components."); 1681 1682 ContainerAdderPolicy.scan!T(locator, storage); 1683 } 1684 1685 /** 1686 ditto 1687 **/ 1688 void scan(alias T, X...)(Storage!(ObjectFactory, string) storage, Locator!() locator) { 1689 1690 scan!T(storage, locator); 1691 1692 static if (X.length > 0) { 1693 1694 scan!X(storage, locator); 1695 } 1696 } 1697 1698 /** 1699 Scan symbol T for possible components using ContainerAdderPolicy 1700 1701 Params: 1702 storage = identity by which to search storage in locator, that will be used to store components 1703 locator = locator of components used to by component factories 1704 **/ 1705 void scan(alias T)(string storage, Locator!() locator) { 1706 1707 scan!T(locator.locate!(Storage!(ObjectFactory, string))(storage), locator); 1708 } 1709 1710 /** 1711 ditto 1712 **/ 1713 void scan(alias T, X...)(string storage, Locator!() locator) { 1714 1715 scan!(T, X)(locator.locate!(Storage!(ObjectFactory, string))(storage), locator); 1716 } 1717 1718 /** 1719 Scan symbol T for possible components using ContainerAdderPolicy 1720 1721 Params: 1722 container = container where to store and from which to locate dependencies for components 1723 **/ 1724 void scan(alias T)(ConfigurableContainer container) { 1725 1726 scan!T(container, container); 1727 } 1728 1729 1730 void scan(alias T, St : Storage!(ObjectFactory, string) = Storage!(ObjectFactory, string))(Locator!() locator) { 1731 1732 scan!T(locator.locate!St, locator); 1733 } 1734 1735 /** 1736 ditto 1737 **/ 1738 void scan(alias T, St : Storage!(ObjectFactory, string), X...)(Locator!() locator) { 1739 1740 scan!(T, St)(locator); 1741 1742 static if (X.length > 0) { 1743 1744 scan!X(locator); 1745 } 1746 } 1747 1748 /** 1749 ditto 1750 **/ 1751 void scan(alias T, X...)(Locator!() locator) { 1752 scan!T(locator); 1753 1754 static if (X.length > 0) { 1755 1756 scan!X(locator); 1757 } 1758 } 1759 } 1760 1761 /** 1762 Default implementation of $(D_INLINECODE scan) family of functions featuring all scanning features provided by library. 1763 **/ 1764 mixin Scanner!(ContainerAdderImpl!()); 1765 1766 /** 1767 A small utility function that will resolve method arguments using a locator and look also for annotations on arguments. 1768 1769 Params: 1770 locator = locator used to prepare list of arguments for function 1771 overload = function for which to prepare arguments. The func itself must not be an overloaded set of functions. 1772 args = list of args that should override existing references by type of argument. 1773 1774 Returns: 1775 a tuple with all required arguments prefilled. 1776 **/ 1777 auto prepare(alias overload, Args...)(Locator!() locator, out Parameters!overload parameters, Args args) { 1778 import std.traits : FunctionTypeOf; 1779 1780 auto references = makeFunctionParameterReferences!(overload)().expand; 1781 static foreach (index, arg; parameters) { 1782 static if (is(Args[index] == typeof(arg))) { 1783 arg = args[index]; 1784 } else { 1785 arg = references[index].resolve!(typeof(arg))(locator); 1786 } 1787 } 1788 } 1789 1790 private alias makeFunctionParameterReferences(alias FunctionType) = aermicioi.aedi.factory.reference.makeFunctionParameterReferences!(FunctionType, transformToReference); 1791 1792 auto transformToReference(string reference, string symbol) { 1793 import aermicioi.aedi.factory.reference : transformToBasic = transformToReference; 1794 import std.range : chain, only, enumerate; 1795 import std.algorithm : joiner, map, substitute; 1796 import std.array : array; 1797 import std.utf : byChar; 1798 1799 string delegate (string) toTypeGen = (s) => "toType!(" ~ s ~ ")"; 1800 string delegate (string, string) typeEnforcedRefGen = (t, s) => "typeEnforcedRef!(" ~ toTypeGen(t) ~ ")(" ~ s ~ ")"; 1801 string delegate (string) lrefGen = (s) => s ~ ".lref"; 1802 string delegate (string, string) alternateGen = (f, s) => f ~ ".alternate(" ~ s ~ ")"; 1803 1804 return transformToBasic(reference, symbol) ~ " 1805 import std.meta : AliasSeq; 1806 import aermicioi.aedi.configurer.annotation.annotation : isQualifierAnnotation; 1807 alias Qualifiers = AliasSeq!(__traits(getAttributes, " ~ symbol ~ ")); 1808 static foreach (index; 0 .. Qualifiers.length) { 1809 static if (isQualifierAnnotation!(Qualifiers[index])) { 1810 " ~ reference ~ " = " ~ alternateGen(typeEnforcedRefGen(symbol, lrefGen("Qualifiers[index].id")), typeEnforcedRefGen(symbol, reference)) ~ "; 1811 } 1812 } 1813 "; 1814 } 1815 1816 private template allUDAs(alias symbol) { 1817 alias allUDAs = AliasSeq!(__traits(getAttributes, symbol)); 1818 }