1 module aermicioi.aedi.factory.deferring_factory; 2 3 import aermicioi.aedi.factory.factory; 4 import aermicioi.aedi.factory.generic_factory; 5 import aermicioi.aedi.storage.decorator; 6 import aermicioi.aedi.factory.decorating_factory : DecoratableGenericFactory; 7 import aermicioi.aedi.util.range : exceptions, filterByInterface; 8 import aermicioi.aedi.util.typecons : AllArgsConstructor, ArgsConstructor; 9 import aermicioi.aedi.exception.circular_reference_exception : CircularReferenceException; 10 import aermicioi.aedi.exception.di_exception : AediException; 11 12 /** 13 Context for configuration actions that were delayed. Automatically will run them once the original component requested is fetched back. 14 **/ 15 @safe class DeferralContext 16 { 17 18 private 19 { 20 21 /** 22 A list of deffered configuration actions. 23 **/ 24 void delegate() @safe[] deferred; 25 26 /** 27 Amount of nested requests for components. Used to track when deffered actions are required to run. 28 **/ 29 size_t requests; 30 } 31 32 public 33 { 34 @property bool pending() inout { 35 import std.range : empty; 36 37 return requests == 0 && !deferred.empty; 38 } 39 40 void track() 41 { 42 ++this.requests; 43 } 44 45 void release() 46 in(requests > 0, "Cannot decrease component request count since it is already at 0.") 47 { 48 --this.requests; 49 } 50 51 void execute() 52 in (requests == 0, "Cannot execute deferred actions while construction of components involved in circular dependency chain is in progress.") { 53 import std.algorithm : reverse; 54 auto pending = this.deferred.reverse; 55 deferred = null; 56 57 foreach (action; pending) 58 { 59 action(); 60 } 61 } 62 63 typeof(this) register(void delegate() @safe deferred) 64 { 65 this.deferred ~= deferred; 66 67 return this; 68 } 69 } 70 } 71 72 @safe class DeferringFactory(T) : DecoratableGenericFactory!T 73 { 74 75 this( 76 GenericFactory!T decorated, 77 DeferralContext context, 78 ) { 79 this.decorated = decorated; 80 this.context = context; 81 } 82 83 private DeferralContext context; 84 85 public 86 { 87 /** 88 Instantiates something of type T. 89 90 Returns: 91 T instantiated component of type T. 92 **/ 93 override T factory() @safe { 94 this.context.track; 95 scope(exit) this.context.release; 96 return super.factory(); 97 } 98 99 /** 100 Adds an configurer to the PropertyConfigurersAware. 101 102 Params: 103 configurer = a configurer that will be invoked after factory of an object. 104 105 Returns: 106 The PropertyConfigurersAware instance 107 **/ 108 override GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) @safe 109 { 110 super.addPropertyConfigurer(new DeferringPropertyConfigurer!T(configurer, this.context)); 111 112 return this; 113 } 114 } 115 } 116 117 @safe class DeferringPropertyConfigurer(T) : PropertyConfigurer!T, Decorator!(PropertyConfigurer!T) { 118 import aermicioi.aedi.storage.locator : Locator; 119 mixin MutableDecoratorMixin!(PropertyConfigurer!T); 120 private DeferralContext context; 121 122 this ( 123 PropertyConfigurer!T configurer, 124 DeferralContext context 125 ) { 126 this.decorated = configurer; 127 this.context = context; 128 } 129 130 public { 131 132 /** 133 Accepts a reference to an object that is to be configured by the configurer. 134 135 Params: 136 object = An object of type T, that will be configured 137 **/ 138 void configure(ref T object) @safe { 139 try { 140 this.decorated.configure(object); 141 } catch (AediException e) { 142 if (context !is null) { 143 foreach (CircularReferenceException exception; e.exceptions.filterByInterface!CircularReferenceException) { 144 T closure = object; 145 context.register(() => this.decorated.configure(closure)); 146 } 147 } 148 } 149 } 150 151 /** 152 Set a locator to object. 153 154 Params: 155 locator = the locator that is set to oject. 156 157 Returns: 158 LocatorAware. 159 **/ 160 @property typeof(this) locator(Locator!() locator) @safe nothrow { 161 this.decorated.locator = locator; 162 163 return this; 164 } 165 } 166 }