1 /** 2 * Contains the implementation of the provider. 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-04-11 8 */ 9 10 module dango.inject.provider; 11 12 private 13 { 14 import std.format : fmt = format; 15 import std.exception : enforce; 16 import std.meta : anySatisfy, AliasSeq, Filter; 17 import std.traits; 18 19 import dango.inject.container: DependencyContainer; 20 import dango.inject.factory : ComponentFactory; 21 import dango.inject.exception : InjectDangoException; 22 import dango.inject.injection : Inject, Named, inject; 23 } 24 25 26 /** 27 * Dependency registration 28 */ 29 class Registration 30 { 31 private 32 { 33 Provider _provider; 34 bool _isNamed; 35 string _name; 36 } 37 38 /** 39 * Main constructor 40 */ 41 this(Provider provider) nothrow @safe 42 { 43 this._provider = provider; 44 this._isNamed = false; 45 } 46 47 /** 48 * Main constructor with name 49 */ 50 this(Provider provider, string name) nothrow @safe 51 { 52 this._provider = provider; 53 this._name = name; 54 this._isNamed = true; 55 } 56 57 /** 58 * Return a `TypeInfo` describing the type provided. 59 */ 60 TypeInfo providedType() const pure nothrow @safe 61 { 62 return _provider.providedType(); 63 } 64 65 /** 66 * Return a `TypeInfo` describing the type registered. 67 */ 68 TypeInfo registeredType() const pure nothrow @safe 69 { 70 return _provider.registeredType(); 71 } 72 73 /** 74 * Return a `string` describing the name. 75 */ 76 string name() const pure nothrow @safe 77 { 78 return _name; 79 } 80 81 /** 82 * Return isNamed registration. 83 */ 84 bool isNamed() const pure nothrow @safe 85 { 86 return _isNamed; 87 } 88 89 90 T provide(T)(bool injectInstance) @safe 91 { 92 T result; 93 _provider.withProvided(injectInstance, (val) @trusted { 94 static if (is(T == interface) || is(T == class)) 95 result = cast(T)(*(cast(Object*)val)); 96 else 97 result = *(cast(T*)val); 98 }); 99 return result; 100 } 101 102 /** 103 * Return maybe singleton registration; 104 */ 105 bool canSingleton() const pure nothrow @safe 106 { 107 return _provider.canSingleton(); 108 } 109 } 110 111 112 /** 113 * Interface for a provider for dependency injection. 114 * A provider knows about the type it produces, and 115 * can produce a value. 116 */ 117 interface Provider 118 { 119 /** 120 * Return a `TypeInfo` describing the type provided. 121 */ 122 TypeInfo providedType() const pure nothrow @safe; 123 124 /** 125 * Return a `TypeInfo` describing the type registered. 126 */ 127 TypeInfo registeredType() const pure nothrow @safe; 128 129 /** 130 * Produce the value. 131 * A pointer to the value is passed to a delegate. 132 * 133 * Notes: The pointer may no longer be valid after `dg` returns, so the value 134 * pointed to should be copied to persist it. 135 */ 136 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe; 137 138 /** 139 * Return maybe singleton provider 140 */ 141 bool canSingleton() const pure nothrow @safe; 142 143 /** 144 * Return original provider 145 */ 146 Provider originalProvider() pure nothrow @safe; 147 } 148 149 150 /** 151 * A `Provider` that provides a value. The value is 152 * provided at construction type and the same value 153 * is returned each time provide is called. 154 */ 155 class ValueProvider(T) : Provider 156 { 157 private T _value; 158 159 /** 160 * Common constructor 161 */ 162 this(T value) nothrow @safe 163 { 164 this._value = value; 165 } 166 167 /** 168 * Return a `TypeInfo` describing the type provided. 169 */ 170 TypeInfo providedType() const pure nothrow @safe 171 { 172 return typeid(T); 173 } 174 175 /** 176 * Return a `TypeInfo` describing the type registered. 177 */ 178 TypeInfo registeredType() const pure nothrow @safe 179 { 180 return typeid(T); 181 } 182 183 /** 184 * Produce the value. 185 * A pointer to the value is passed to a delegate. 186 * 187 * Notes: The pointer may no longer be valid after `dg` returns, so the value 188 * pointed to should be copied to persist it. 189 */ 190 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe 191 { 192 dg(&_value); 193 } 194 195 /** 196 * Return maybe singleton provider 197 */ 198 bool canSingleton() const pure nothrow @safe 199 { 200 return false; 201 } 202 203 /** 204 * Return original provider 205 */ 206 Provider originalProvider() pure nothrow @safe 207 { 208 return this; 209 } 210 } 211 212 @("Should work value provider") 213 @system unittest 214 { 215 auto provider = new ValueProvider!int(10); 216 assert (provider.providedType == typeid(int)); 217 assert (provider._value == 10); 218 } 219 220 @("Should work value provider existing class") 221 @system unittest 222 { 223 class C {} 224 auto c = new C(); 225 auto provider = new ValueProvider!C(c); 226 assert (provider.providedType == typeid(C)); 227 assert (provider._value is c); 228 } 229 230 231 /** 232 * A provider that uses a factory to get the value. 233 */ 234 class FactoryProvider(F : ComponentFactory!(T, A), T, A...) : Provider 235 { 236 private 237 { 238 DependencyContainer _container; 239 F _factory; 240 A _args; 241 } 242 243 /** 244 * Main constructor 245 */ 246 this(DependencyContainer container, F factory, A args) 247 { 248 this._container = container; 249 this._factory = factory; 250 this._args = args; 251 } 252 253 /** 254 * Return a `TypeInfo` describing the type provided. 255 */ 256 TypeInfo providedType() const nothrow @safe 257 { 258 return typeid(T); 259 } 260 261 /** 262 * Return a `TypeInfo` describing the type registered. 263 */ 264 TypeInfo registeredType() const pure nothrow @safe 265 { 266 return typeid(T); 267 } 268 269 /** 270 * Produce the value. 271 * A pointer to the value is passed to a delegate. 272 * 273 * Notes: The pointer may no longer be valid after `dg` returns, so the value 274 * pointed to should be copied to persist it. 275 */ 276 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @trusted 277 { 278 static if (is(T == class) || is (T == interface)) 279 { 280 T instance = _factory.createComponent(_args); 281 Object result = cast(Object)instance; 282 } 283 else 284 T result = _factory.createComponent(_args); 285 286 dg(&result); 287 } 288 289 /** 290 * Return maybe singleton provider 291 */ 292 bool canSingleton() const pure nothrow @safe 293 { 294 return false; 295 } 296 297 /** 298 * Return original provider 299 */ 300 Provider originalProvider() pure nothrow @safe 301 { 302 return this; 303 } 304 } 305 306 @("Should work factory provider") 307 @system unittest 308 { 309 interface Animal { string name() @safe; } 310 class Cat : Animal { 311 private string _name; 312 this(string name) @safe { _name = name; } 313 string name() @safe { return _name; } 314 } 315 class TestFactory : ComponentFactory!(Animal, string) 316 { 317 Animal createComponent(string name) @safe 318 { 319 return new Cat(name); 320 } 321 } 322 323 auto container = new DependencyContainer(); 324 auto factory = new TestFactory(); 325 Provider provider = new FactoryProvider!(TestFactory)(container, factory, "cat"); 326 assert (provider.providedType == typeid(Animal)); 327 auto reg = new Registration(provider); 328 auto result = reg.provide!Animal(false); 329 assert (result.name == "cat"); 330 } 331 332 333 /** 334 * A Provider that instantiates instances of a class. 335 * 336 * Arguments to the constructor are resolved using a `Resolver` (typically a `Container`). 337 * If an `Injectable` annotation is on the class, then the template arguments to the `Injectable` 338 * determine how the injected arguments should be resolved. Otherwise, the argument types for the 339 * first constructor are used. 340 */ 341 class ClassProvider(I, T : I) : Provider 342 if (is(T == class)) 343 { 344 private 345 { 346 DependencyContainer _container; 347 bool isBeingInjected; 348 } 349 350 /** 351 * Main constructor 352 */ 353 this(DependencyContainer container) @safe nothrow 354 { 355 this._container = container; 356 } 357 358 /** 359 * Return a `TypeInfo` describing the type provided. 360 */ 361 TypeInfo providedType() const pure nothrow @safe 362 { 363 return typeid(T); 364 } 365 366 /** 367 * Return a `TypeInfo` describing the type registered. 368 */ 369 TypeInfo registeredType() const pure nothrow @safe 370 { 371 return typeid(I); 372 } 373 374 /** 375 * Produce the value. 376 * A pointer to the value is passed to a delegate. 377 * 378 * Notes: The pointer may no longer be valid after `dg` returns, so the value 379 * pointed to should be copied to persist it. 380 */ 381 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @trusted 382 { 383 enforce!InjectDangoException(_container, 384 "A dependency container is not defined. Cannot perform constructor" ~ 385 " injection without one."); 386 enforce!InjectDangoException(!isBeingInjected, 387 fmt!("%s is already being created and injected; possible circular" ~ 388 "dependencies in constructors?")(T.stringof)); 389 390 V resolveMember(V, P...)() 391 { 392 enum IsNamed(alias N) = is(typeof(N) == Named); 393 enum named = Filter!(IsNamed, P); 394 static if (named.length) 395 { 396 enum name = named[0].name; 397 static if (isDynamicArray!V && !isSomeString!V) 398 return _container.resolveAll!(ForeachType!V)(name); 399 else 400 return _container.resolve!V(name); 401 } 402 else 403 { 404 static if (isDynamicArray!V && !isSomeString!V) 405 return _container.resolveAll!(ForeachType!V); 406 else 407 return _container.resolve!V; 408 } 409 } 410 411 template IsCtorInjected(alias ctor) 412 { 413 template IsInjectAttribute(alias A) 414 { 415 enum IsInjectAttribute = (is(A == Inject!Q, Q) || 416 __traits(isSame, A, Inject)); 417 } 418 enum IsCtorInjected = anySatisfy!(IsInjectAttribute, 419 __traits(getAttributes, ctor)); 420 } 421 422 T instance = null; 423 static if (__traits(compiles, __traits(getOverloads, T, `__ctor`))) 424 { 425 foreach(ctor ; __traits(getOverloads, T, `__ctor`)) 426 { 427 static if (IsCtorInjected!ctor) 428 { 429 enum CtorLen = __traits(getAttributes, ctor).length; 430 alias Params = Parameters!ctor; 431 isBeingInjected = true; 432 scope(exit) 433 isBeingInjected = false; 434 Params params = void; 435 foreach (i, param; Params) 436 { 437 alias SP = Params[i..i+1]; 438 static if (__traits(compiles, __traits(getAttributes, SP))) 439 { 440 alias Attr = AliasSeq!(__traits(getAttributes, SP)); 441 static if (Attr.length) 442 params[i] = resolveMember!(param, Attr[CtorLen..Attr.length]); 443 else 444 params[i] = resolveMember!(param, Attr); 445 } 446 else 447 params[i] = resolveMember!(param); 448 } 449 instance = new T(params); 450 break; 451 } 452 } 453 } 454 455 if (instance is null) 456 instance = cast(T)(typeid(T).create()); 457 458 enforce!InjectDangoException(instance !is null, 459 "Unable to create instance of type" ~ T.stringof ~ 460 ", does it have injectable constructors?"); 461 462 if (injectInstance) 463 _container.inject!T(instance); 464 465 dg(&instance); 466 } 467 468 /** 469 * Return maybe singleton provider 470 */ 471 bool canSingleton() const pure nothrow @safe 472 { 473 return true; 474 } 475 476 /** 477 * Return original provider 478 */ 479 Provider originalProvider() pure nothrow @safe 480 { 481 return this; 482 } 483 } 484 485 486 version (unittest) 487 { 488 import std.exception : assertThrown; 489 interface Printer { string name() @safe; } 490 class LaserPrinter : Printer { 491 string name() @safe { return "LaserJet 2000"; } 492 } 493 struct Page {} 494 interface Report { 495 string print() @safe; 496 Page[] pages() @safe; 497 string title() @safe; 498 } 499 abstract class BaseReport : Report 500 { 501 Page[] pages() @safe { return []; } 502 string title() @safe { return "t"; } 503 } 504 class SimpleReport : BaseReport { 505 string print() @safe { return "simple"; } 506 } 507 class ErrorReport : BaseReport { 508 @Inject 509 this(Report parent) @safe {} 510 string print() @safe { return "error"; } 511 } 512 class AdditionalReport : BaseReport { 513 private { 514 Printer _printer; 515 string _title; 516 Page[] _pages; 517 } 518 @Inject 519 this(Printer printer, Page[] pages, @Named("title") string title) 520 { 521 this._printer = printer; 522 this._title = title; 523 this._pages = pages; 524 } 525 string print() @safe { return _printer.name; } 526 override Page[] pages() @safe { return _pages; } 527 override string title() @safe { return _title; } 528 } 529 } 530 531 @("Should work register concrete named class") 532 @system unittest 533 { 534 auto cont = new DependencyContainer(); 535 auto reg1 = cont.register!SimpleReport("r1"); 536 auto reg2 = cont.register!SimpleReport("r2"); 537 assert (reg1 != reg2); 538 assertThrown(cont.resolve!Report); 539 auto reps = cont.resolveAll!SimpleReport; 540 assert (reps.length == 2); 541 auto report = cont.resolve!SimpleReport("r1"); 542 assert (report && report.print == "simple"); 543 } 544 545 @("Should work resolve class by circular constructor") 546 @system unittest 547 { 548 auto cont = new DependencyContainer(); 549 cont.register!(Report, ErrorReport); 550 assertThrown(cont.resolve!Report); 551 } 552 553 @("Should work resolve class by constructor") 554 @system unittest 555 { 556 auto cont = new DependencyContainer(); 557 cont.register!(Printer, LaserPrinter); 558 cont.value("p1", Page()); 559 cont.value("p2", Page()); 560 cont.value("title", "printer"); 561 cont.register!(Report, AdditionalReport); 562 auto rep = cont.resolve!(Report); 563 assert (rep); 564 assert (rep.print == "LaserJet 2000"); 565 assert (rep.pages.length == 2); 566 assert (rep.title == "printer"); 567 } 568 569 570 /** 571 * A Provider that uses another provider to create an instance 572 * the first time `provide` is called. Future calls to `provide` 573 * will return this same instance. 574 */ 575 class SingletonProvider : Provider 576 { 577 private 578 { 579 Provider _original; 580 void* _instance; 581 } 582 583 /** 584 * Common constructor 585 */ 586 this(Provider orig) pure nothrow @safe 587 { 588 this._original = orig; 589 } 590 591 /** 592 * Return a `TypeInfo` describing the type provided. 593 */ 594 TypeInfo providedType() const pure nothrow @safe 595 { 596 return _original.providedType(); 597 } 598 599 /** 600 * Return a `TypeInfo` describing the type registered. 601 */ 602 TypeInfo registeredType() const pure nothrow @safe 603 { 604 return _original.registeredType(); 605 } 606 607 /** 608 * Produce the value. 609 * A pointer to the value is passed to a delegate. 610 * 611 * Notes: The pointer may no longer be valid after `dg` returns, so the value 612 * pointed to should be copied to persist it. 613 */ 614 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe 615 { 616 if (_instance is null) 617 { 618 synchronized (this) 619 { 620 if (_instance is null) 621 createInstance(injectInstance); 622 } 623 } 624 dg(_instance); 625 } 626 627 /** 628 * Return maybe singleton provider 629 */ 630 bool canSingleton() const pure nothrow @safe 631 { 632 return false; 633 } 634 635 /** 636 * Return original provider 637 */ 638 Provider originalProvider() pure nothrow @safe 639 { 640 return _original; 641 } 642 643 /** 644 * Create an instance using the base provider. 645 * 646 * Since we don't know if the value is allocated on the stack 647 * or the heap, we need to allocate space on the heap and copy it 648 * there. 649 */ 650 private void createInstance(bool injectInstance) @trusted 651 { 652 import core.memory : GC; 653 import core.stdc.string : memcpy; 654 auto info = _original.providedType(); 655 _original.withProvided(injectInstance, (void* ptr) @trusted { 656 if (ptr !is null) 657 { 658 _instance = GC.malloc(info.tsize, GC.getAttr(ptr), info); 659 memcpy(_instance, ptr, info.tsize); 660 } 661 }); 662 info.postblit(_instance); 663 } 664 } 665 666 @("Should work SingletonProvider") 667 @system unittest 668 { 669 class BaseProvider : Provider 670 { 671 private int counter = 0; 672 673 TypeInfo providedType() const pure nothrow @safe 674 { 675 return typeid(int); 676 } 677 678 TypeInfo registeredType() const pure nothrow @safe 679 { 680 return typeid(int); 681 } 682 683 void withProvided(bool ii, void delegate(void*) @safe dg) @safe 684 { 685 counter++; 686 dg(&counter); 687 } 688 689 bool canSingleton() const pure nothrow @safe 690 { 691 return false; 692 } 693 694 Provider originalProvider() pure nothrow @safe 695 { 696 return this; 697 } 698 } 699 700 auto provider = new SingletonProvider(new BaseProvider()); 701 assert (provider.providedType == typeid(int)); 702 int first; 703 provider.withProvided(false, (void* val) @trusted { 704 first = *(cast(int*)val); 705 }); 706 assert (first == 1); 707 provider.withProvided(false, (void* val) @trusted { 708 first = *(cast(int*)val); 709 }); 710 assert (first == 1); 711 } 712 713 714 /** 715 * Scopes registrations to return the same instance every time a given registration is resolved. 716 * 717 * Effectively makes the given registration a singleton. 718 */ 719 Registration singleInstance(Registration registration) nothrow @safe 720 { 721 if (registration.canSingleton) 722 registration._provider = new SingletonProvider(registration._provider); 723 return registration; 724 } 725 726 727 /** 728 * A Provider that uses another provider to existing instance 729 */ 730 class ExistingInstanceProvider(I) : Provider 731 if (is(I == class)) 732 { 733 private 734 { 735 Provider _original; 736 I _instance; 737 } 738 739 /** 740 * Common constructor 741 */ 742 this(I instance, Provider original) pure nothrow @safe 743 { 744 this._original = original; 745 this._instance = instance; 746 } 747 748 /** 749 * Return a `TypeInfo` describing the type provided. 750 */ 751 TypeInfo providedType() const pure nothrow @safe 752 { 753 return _original.providedType(); 754 } 755 756 /** 757 * Return a `TypeInfo` describing the type registered. 758 */ 759 TypeInfo registeredType() const pure nothrow @safe 760 { 761 return _original.registeredType(); 762 } 763 764 /** 765 * Produce the value. 766 * A pointer to the value is passed to a delegate. 767 * 768 * Notes: The pointer may no longer be valid after `dg` returns, so the value 769 * pointed to should be copied to persist it. 770 */ 771 void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe 772 { 773 dg(&_instance); 774 } 775 776 /** 777 * Return maybe singleton provider 778 */ 779 bool canSingleton() const pure nothrow @safe 780 { 781 return false; 782 } 783 784 /** 785 * Return original provider 786 */ 787 Provider originalProvider() pure nothrow @safe 788 { 789 return _original; 790 } 791 } 792 793 794 /** 795 * Scopes registrations to return a new instance every time the given registration is resolved. 796 */ 797 Registration newInstance(Registration registration) nothrow @safe 798 { 799 registration._provider = registration._provider.originalProvider(); 800 return registration; 801 } 802 803 @("Should work singleInstance and newInstance") 804 @safe unittest 805 { 806 class Counter { private int counter; } 807 auto cont = new DependencyContainer(); 808 auto reg = cont.register!(Counter).singleInstance; 809 auto cnt = cont.resolve!Counter; 810 cnt.counter++; 811 cnt = cont.resolve!Counter; 812 assert (cnt.counter == 1); 813 reg.newInstance; 814 cnt = cont.resolve!Counter; 815 assert (cnt.counter == 0); 816 } 817 818 819 /** 820 * Scopes registrations to return the given instance every time the given registration is resolved. 821 */ 822 Registration existingInstance(T)(Registration registration, T instance) @trusted 823 if (is(T == class)) 824 { 825 if (!canBuiltinInheritance!T(registration.providedType)) 826 throw new InjectDangoException(fmt!"Type '%s' not provide '%s'"( 827 registration.providedType, typeid(T))); 828 829 if (!registration.canSingleton) 830 throw new InjectDangoException(fmt!"Type '%s' not support singleton"( 831 registration.providedType)); 832 833 registration._provider = new ExistingInstanceProvider!T(instance, registration._provider); 834 return registration; 835 } 836 837 @("Should work existingInstance") 838 @safe unittest 839 { 840 class Counter { 841 private int _counter; 842 this(int c) { _counter = c; } 843 } 844 auto cont = new DependencyContainer(); 845 auto counter = new Counter(3); 846 cont.register!(Counter).existingInstance(counter); 847 auto rcnt = cont.resolve!Counter; 848 assert (rcnt._counter == 3); 849 } 850 851 852 /** 853 * Check TypeInfo 854 */ 855 private bool canBuiltinInheritance(T)(TypeInfo info) nothrow @trusted 856 if (is(T == class)) 857 { 858 import std.traits : BaseClassesTuple, InterfacesTuple; 859 import std.meta : AliasSeq, staticMap, Filter, staticIndexOf; 860 861 template ToTypeInfo(A) 862 { 863 enum ToTypeInfo = typeid(A); 864 } 865 866 template IsTypeInfo(A) 867 { 868 enum IsTypeInfo = !is(A == Object); 869 } 870 871 enum Infos = staticMap!(ToTypeInfo, T, Filter!(IsTypeInfo, 872 AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T))); 873 874 try 875 foreach (typeInfo; Infos) 876 { 877 if (typeInfo == info) 878 return true; 879 } 880 catch (Exception e) 881 return false; 882 883 return false; 884 } 885 886 @("Should work canBuiltinInheritance") 887 @safe unittest 888 { 889 interface TestT {} 890 interface TestA {} 891 interface TestB : TestA {} 892 abstract class TestC : TestB {} 893 class TestD : TestC {} 894 class TestE : TestD {} 895 assert (!canBuiltinInheritance!TestE(typeid(int))); 896 assert (canBuiltinInheritance!TestE(typeid(TestA))); 897 } 898