1 /** 2 * Contains functionality for autowiring dependencies using a dependency container. 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-16 8 */ 9 10 module dango.inject.injection; 11 12 private 13 { 14 import std.traits; 15 16 import dango.inject.container; 17 } 18 19 20 struct Inject(QualifierType) {} 21 22 23 struct Named 24 { 25 string name; 26 } 27 28 29 void inject(Type)(DependencyContainer container, Type instance) @safe 30 { 31 static if (BaseClassesTuple!Type.length > 1) 32 inject!(BaseClassesTuple!Type[0])(container, instance); 33 34 static foreach (name; FieldNameTuple!Type) 35 injectMember!(name, Type)(container, instance); 36 } 37 38 version (unittest) 39 { 40 interface Printer { string title() @safe; } 41 class LaserPrinter : Printer { 42 private string _title; 43 @Inject 44 this(@Named("printer") string title) { _title = title; } 45 string title() @safe { return _title; } 46 } 47 interface Report {} 48 abstract class SimpleReport : Report { 49 @Inject!LaserPrinter 50 private Printer _printer; 51 @Inject @Named("pages") 52 private int _pages; 53 @Inject @Named("report") 54 private string _title; 55 @Inject 56 private ubyte[] _code; 57 @Inject 58 private Printer _printer2 = new LaserPrinter("superlaser"); 59 } 60 class AdditionalReport : SimpleReport {} 61 } 62 63 @("Should work inject to object") 64 @safe unittest 65 { 66 auto cont = new DependencyContainer(); 67 cont.register!(Printer, LaserPrinter); 68 cont.value!int("pages", 2); 69 cont.value!string("printer", "laser"); 70 cont.value!string("report", "matrix"); 71 cont.value!string("other"); 72 cont.value!ubyte(1); 73 cont.value!ubyte(2); 74 cont.register!(Report, AdditionalReport); 75 auto rep = cast(SimpleReport)cont.resolve!Report(); 76 assert (rep); 77 assert (rep._printer2.title == "superlaser"); 78 assert (rep._printer && rep._printer.title == "laser"); 79 assert (rep._title == "matrix"); 80 assert (rep._code == [1, 2]); 81 } 82 83 84 private: 85 86 87 void injectMember(string name, Type)(DependencyContainer container, Type instance) @safe 88 { 89 alias member = __traits(getMember, Type, name); 90 static foreach (attribute; __traits(getAttributes, member)) 91 { 92 static if (is(attribute == Inject!T, T : typeof(member))) 93 injectMemberType!(name, Type, typeof(member), T)(container, instance); 94 else static if (__traits(isSame, attribute, Inject)) 95 injectMemberType!(name, Type, typeof(member))(container, instance); 96 } 97 } 98 99 100 void injectMemberType(string name, Type, MemberTypes...)(DependencyContainer container, Type instance) @safe 101 { 102 alias MemberType = MemberTypes[0]; 103 static if (is(MemberType == class) || is(MemberType == interface)) 104 if (__traits(getMember, instance, name) !is null) 105 return; 106 107 static if (isDynamicArray!MemberType && !isSomeString!MemberType) 108 injectMultiple!(name, Type, MemberTypes)(container, instance); 109 else 110 injectSingle!(name, Type, MemberTypes)(container, instance); 111 } 112 113 114 void injectMultiple(string name, Type, MemberTypes...)(DependencyContainer container, Type instance) @safe 115 { 116 alias MemberElementType = ForeachType!(MemberTypes[0]); 117 enum namedUDAs = getUDAs!(__traits(getMember, Type, name), Named); 118 static if (namedUDAs.length) 119 __traits(getMember, instance, name) = container.resolveAll!(MemberElementType)(namedUDAs[0].name); 120 else 121 __traits(getMember, instance, name) = container.resolveAll!(MemberElementType)(); 122 } 123 124 125 void injectSingle(string name, Type, MemberTypes...)(DependencyContainer container, Type instance) @safe 126 { 127 enum namedUDAs = getUDAs!(__traits(getMember, Type, name), Named); 128 static if (namedUDAs.length) 129 __traits(getMember, instance, name) = container.resolve!(MemberTypes)(namedUDAs[0].name); 130 else 131 { 132 auto ins = container.resolve!(MemberTypes)(); 133 assert (ins); 134 __traits(getMember, instance, name) = container.resolve!(MemberTypes)(); 135 } 136 } 137