1 /++
2 This module adds declares a mixin template for automatically generating class constructor.
3 Generated constructor takes 1 argument per each field declared with `@wired` attribute. Later each argument gets assigned to its respective field.
4 Examples:
5 ---
6 class A {
7 	@wired {
8 		int a;
9 		string b;
10 		float c;
11 	}
12 
13 	mixin wire;
14 }
15 auto a = new A(1, "hi", 42.0);
16 assert(a.a == 1 && a.b == "hi" && a.c == 42.0);
17 ---
18 +/
19 module dc.wire;
20 
21 import dc : from;
22 import dc.dotbuilder : d = dotBuilder;
23 
24 unittest {
25 	static assert(!__traits(compiles, {
26 		struct B {
27 			@wired ubyte a;
28 
29 			mixin wire;
30 		}
31 	}));
32 
33 	class A {
34 		@wired {
35 			int a;
36 			string b;
37 			float c;
38 		}
39 
40 		mixin wire;
41 	}
42 	auto a = new A(1, "hi", 42.0);
43 	assert(a.a == 1 && a.b == "hi" && a.c == 42.0);
44 }
45 /// An attribute to decorate fields about which `wire` should care
46 enum wired;
47 
48 /// Mixin template that generates our class constructor
49 mixin template wire() {
50 	static assert(is(typeof(this) == class), from!(d.std.traits).fullyQualifiedName!(typeof(this)) ~ " is not a class");
51 	private {
52 		template WiredFields(T) {
53 			import std.traits : hasUDA, FieldNameTuple;
54 			import std.meta : Filter;
55 			enum bool isWiredField(alias field) = hasUDA!(__traits(getMember, T, field), wired);
56 			alias WiredFields = Filter!(isWiredField, FieldNameTuple!T);
57 		}
58 
59 		template WiredTypes(T) {
60 			import std.meta : staticMap;
61 			alias GetType(alias field) = typeof(__traits(getMember, T, field));
62 			alias WiredTypes = staticMap!(GetType, WiredFields!T);
63 		}
64 	}
65 
66 	this(WiredTypes!(typeof(this)) args) {
67 		alias This = typeof(this);
68 		alias fields = WiredFields!This;
69 		static foreach(i, field; fields) {
70 			__traits(getMember, this, field) = args[i];
71 		}
72 	}
73 }