Line data Source code
1 : library injector_x; 2 : 3 : class InjectionNotFound implements Exception { 4 1 : InjectionNotFound(this.message); 5 : final String message; 6 : } 7 : 8 : class DuplicateInjectionFound implements Exception { 9 1 : DuplicateInjectionFound(this.message); 10 : final String message; 11 : } 12 : 13 : abstract class INeedle<T> { 14 : Type getType(); 15 : T? getMock(); 16 : bool isNewInstance(); 17 : } 18 : 19 : class Needle<T> implements INeedle<T> { 20 : late Type type; 21 : final bool newInstance; 22 3 : Needle({this.newInstance = false}) { 23 3 : type = T; 24 : } 25 : 26 3 : @override 27 : T? getMock() { 28 : return null; 29 : } 30 : 31 3 : @override 32 : Type getType() { 33 3 : return this.type; 34 : } 35 : 36 3 : @override 37 : bool isNewInstance() { 38 3 : return this.newInstance; 39 : } 40 : } 41 : 42 : class NeedleMock<T> implements INeedle<T> { 43 : late Type type; 44 : final T mock; 45 : 46 3 : NeedleMock({Type? type, required this.mock}) { 47 : if (type == null) { 48 3 : this.type = T; 49 : } else { 50 1 : this.type = type; 51 : } 52 : } 53 : 54 3 : @override 55 : T? getMock() { 56 3 : return mock; 57 : } 58 : 59 3 : @override 60 : Type getType() { 61 3 : return this.type; 62 : } 63 : 64 1 : @override 65 : bool isNewInstance() { 66 : return true; 67 : } 68 : } 69 : 70 : abstract class Injectable { 71 : final List<INeedle>? needles; 72 3 : Injectable({this.needles}); 73 : } 74 : 75 : typedef InjectableAdd<T> = T Function(); 76 : 77 : class _InjectStore<T extends Object> { 78 : final String key; 79 : final InjectableAdd<T> injectable; 80 : final bool isSingleton; 81 : dynamic singleton; 82 3 : _InjectStore({ 83 : required this.key, 84 : required this.singleton, 85 : required this.injectable, 86 : required this.isSingleton, 87 : }); 88 : } 89 : 90 : class InjectorXBind { 91 9 : static final List<_InjectStore> _store = []; 92 : 93 3 : static bool _checkKeyExists(String key) { 94 18 : return _store.where((e) => e.key == key).isNotEmpty; 95 : } 96 : 97 3 : static void add<T extends Object>(InjectableAdd<T> injectable, 98 : {bool singleton = false}) { 99 6 : if (_checkKeyExists(T.toString())) { 100 3 : throw DuplicateInjectionFound("${T.toString()} is duplicate."); 101 : } 102 9 : _store.add(_InjectStore<T>( 103 3 : key: T.toString(), 104 : singleton: null, 105 : injectable: injectable, 106 : isSingleton: singleton, 107 : )); 108 : } 109 : 110 1 : static T get<T extends Object>({bool newInstance = false}) { 111 : try { 112 1 : return getByType(T, newInstance: newInstance) as T; 113 1 : } on Exception catch (e) { 114 : throw e; 115 : } 116 : } 117 : 118 3 : static dynamic getByType(Type type, {bool newInstance = false}) { 119 6 : if (!_checkKeyExists(type.toString())) { 120 3 : throw InjectionNotFound("Injection not found from ${type.toString()}."); 121 : } 122 : 123 21 : var ref = _store.where((e) => e.key == type.toString()).first; 124 : 125 3 : if (!ref.isSingleton) { 126 6 : return ref.injectable(); 127 : } else { 128 : if (!newInstance) { 129 1 : var singleton = ref.singleton; 130 : if (singleton == null) { 131 3 : ref.singleton = ref.injectable(); 132 : } 133 1 : return ref.singleton; 134 : } else { 135 2 : return ref.injectable(); 136 : } 137 : } 138 : } 139 : } 140 : 141 : class InjectorX { 142 3 : InjectorX(this.injectNeedles) { 143 6 : for (var needle in injectNeedles) { 144 3 : if (needle.getMock() != null) { 145 15 : _refs[needle.getType().toString()] = needle.getMock(); 146 : } else { 147 6 : var obj = InjectorXBind.getByType(needle.getType(), 148 3 : newInstance: needle.isNewInstance()); 149 12 : _refs[needle.getType().toString()] = obj; 150 : } 151 : } 152 : } 153 : 154 : final List<INeedle> injectNeedles; 155 : final Map<String, dynamic> _refs = {}; 156 : 157 3 : T get<T>() { 158 3 : var key = T.toString(); 159 6 : if (_refs.containsKey(key)) { 160 9 : return _refs[T.toString()]; 161 : } else { 162 3 : print("The injection reference ${T.toString()} is not found"); 163 1 : throw InjectionNotFound( 164 2 : "The injection reference ${T.toString()} is not found"); 165 : } 166 : } 167 : } 168 : 169 : abstract class Inject<T extends Inject<T>> extends Injectable { 170 6 : Inject({List<Needle>? needles}) : super(needles: needles) { 171 7 : this.injector(InjectorX(needles ?? [])); 172 : } 173 : 174 3 : T injectMocks(List<NeedleMock> needleMocks) { 175 3 : var localNeedles = <INeedle>[]; 176 3 : if (this.needles != null) { 177 6 : for (var arg in needleMocks) { 178 : localNeedles = this 179 3 : .needles! 180 6 : .where((e) => 181 9 : e.getType().toString() != 182 9 : arg.type.toString().replaceAll("Mock", "")) 183 3 : .toList(); 184 : } 185 : } 186 : 187 3 : needleMocks.addAll( 188 3 : localNeedles.map((e) => NeedleMock(type: e.getType(), mock: null))); 189 6 : this.injector(InjectorX(needleMocks)); 190 : return this as T; 191 : } 192 : 193 : void injector(InjectorX handler); 194 : }