1 /* 2 Meta utils. 3 */ 4 module automapper.meta; 5 6 7 package: 8 9 import std.traits; 10 public import std.meta; 11 12 /** 13 Get an alias on the member type (work with nested member like "foo.bar"). 14 Params: 15 T = the type where the member is 16 members = the member to alias 17 */ 18 template MemberType(T, string members) 19 { 20 import std..string : split, join; 21 enum string[] memberSplited = members.split("."); 22 23 static if (is(T t == T)) { 24 static if (memberSplited.length > 1) 25 alias MemberType = MemberType!(MemberType!(T, memberSplited[0]), memberSplited[1..$].join(".")); 26 else 27 alias MemberType = typeof(__traits(getMember, t, members)); 28 } 29 } 30 31 /// 32 unittest 33 { 34 static class A { 35 int bar; 36 string baz; 37 } 38 39 static class B { 40 A foo = new A(); 41 } 42 43 static assert(is(MemberType!(A, "bar") == int)); 44 static assert(is(MemberType!(A, "baz") == string)); 45 static assert(is(MemberType!(B, "foo.bar") == int)); 46 } 47 48 /** Test existance of the nested member (or not nested). 49 Params: 50 T = The type where member are nested 51 members = the member to test (e.g. "foo.bar.baz") */ 52 template hasNestedMember(T, string members) 53 { 54 import std..string : split, join; 55 enum string[] memberSplited = members.split("."); 56 57 static if (is(T t == T)) { 58 static if (memberSplited.length > 1) 59 static if (__traits(hasMember, T, memberSplited[0])) 60 enum bool hasNestedMember = hasNestedMember!(MemberType!(T, memberSplited[0]), memberSplited[1..$] 61 .join(".")); 62 else 63 enum bool hasNestedMember = false; 64 else 65 enum bool hasNestedMember = __traits(hasMember, t, members); 66 } 67 } 68 69 /// 70 unittest 71 { 72 static class A { 73 int bar; 74 } 75 76 static class B { 77 A foo = new A(); 78 } 79 80 static class C { 81 B bar = new B(); 82 } 83 84 static assert(hasNestedMember!(B, "foo.bar")); 85 static assert(!hasNestedMember!(B, "data.bar")); 86 static assert(!hasNestedMember!(B, "foo.baz")); 87 static assert(hasNestedMember!(B, "foo")); 88 static assert(!hasNestedMember!(B, "fooz")); 89 static assert(!hasNestedMember!(C, "bar.foo.baz")); 90 static assert(hasNestedMember!(C, "bar.foo.bar")); 91 } 92 93 template isPublicMember(T, string M) 94 { 95 import std.algorithm, std.typetuple : TypeTuple; 96 97 static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) enum isPublicMember = false; 98 else { 99 alias MEM = TypeTuple!(__traits(getMember, T, M)); 100 static if (__traits(compiles, __traits(getProtection, MEM))) 101 enum isPublicMember = __traits(getProtection, MEM).among("public", "export"); 102 else 103 enum isPublicMember = true; 104 } 105 } 106 /// 107 unittest 108 { 109 static class A { 110 public int bar; 111 protected string foo; 112 private int baz; 113 } 114 115 static assert(isPublicMember!(A, "bar")); 116 static assert(!isPublicMember!(A, "foo")); 117 static assert(!isPublicMember!(A, "baz")); 118 } 119 120 /// 121 string GetMember(alias T, string member)() 122 { 123 import std.format : format; 124 return (q{%s.%s}.format(T.stringof, member)); 125 } 126 127 /// 128 unittest 129 { 130 static class A { 131 int bar; 132 } 133 134 static class B { 135 A foo = new A(); 136 } 137 138 auto b = new B(); 139 140 mixin(GetMember!(b, "foo.bar")) = 42; 141 } 142 143 /** Get a list of all public class member. 144 Params: 145 T = The class where to list member */ 146 template ClassMembers(T) if (isClassOrStruct!T) 147 { 148 import std.algorithm : canFind; 149 150 static immutable MembersToIgnore = [__traits(allMembers, Object)]; 151 152 private template ClassMembersImpl(size_t idx) 153 { 154 static if (idx < [__traits(allMembers, T)].length) { 155 enum M = __traits(allMembers, T)[idx]; 156 static if (!isCallable!M && !MembersToIgnore.canFind(M) && isPublicMember!(T, M)) 157 enum string[] ClassMembersImpl = M ~ ClassMembersImpl!(idx + 1); 158 else 159 enum string[] ClassMembersImpl = ClassMembersImpl!(idx + 1); // skip 160 } 161 else { 162 enum string[] ClassMembersImpl = []; 163 } 164 } 165 166 enum string[] ClassMembers = ClassMembersImpl!0; 167 } 168 169 170 /// 171 unittest 172 { 173 static class Base { 174 int baz; 175 } 176 177 static class A : Base { 178 int bar; 179 string foo; 180 private int priv; 181 } 182 183 static assert(ClassMembers!A == ["bar", "foo", "baz"]); 184 static assert(ClassMembers!(A) != ["bar", "foo"]); 185 } 186 187 /** Get a list of flatenned class member. */ 188 template FlattenedMembers(T) if (isClassOrStruct!T) 189 { 190 import std..string : join; 191 192 private template FlattenedMembersImpl(U, size_t idx, string Prefix) 193 { 194 static if (idx < ClassMembers!U.length) { 195 enum M = ClassMembers!U[idx]; 196 enum P = (Prefix == "" ? "" : Prefix ~ "."); // prefix 197 198 // it's a class: recurse 199 static if (isClassOrStruct!(MemberType!(U, M))) 200 enum string[] FlattenedMembersImpl = (P ~ M) ~ 201 FlattenedMembersImpl!(MemberType!(U, M), 0, P ~ M) ~ 202 FlattenedMembersImpl!(U, idx + 1, Prefix); 203 else 204 enum string[] FlattenedMembersImpl = P ~ M ~ FlattenedMembersImpl!(U, idx + 1, Prefix); 205 } 206 else { 207 enum string[] FlattenedMembersImpl = []; 208 } 209 } 210 211 enum string[] FlattenedMembers = FlattenedMembersImpl!(T, 0, ""); 212 } 213 214 /// 215 unittest 216 { 217 static class A { 218 int bar; 219 string str; 220 private int dum; 221 } 222 223 static class B { 224 A foo; 225 int mid; 226 } 227 228 static class C { 229 B baz; 230 int top; 231 } 232 233 static class Address { 234 int zipcode; 235 } 236 237 static class D { 238 Address address; 239 } 240 241 static struct E { 242 int foo; 243 } 244 245 static struct F { 246 E bar; 247 } 248 249 static assert(FlattenedMembers!C == ["baz", "baz.foo", "baz.foo.bar", "baz.foo.str", "baz.mid", "top"]); 250 static assert(FlattenedMembers!D == ["address", "address.zipcode"]); 251 static assert(FlattenedMembers!F == ["bar", "bar.foo"]); 252 } 253 254 template Alias(alias T) 255 { 256 alias Alias = T; 257 } 258 259 template isClass(T) 260 { 261 enum bool isClass = (is(T == class)); 262 } 263 264 template isClassOrStruct(T) 265 { 266 enum bool isClassOrStruct = (is(T == class) || is(T == struct)); 267 } 268 269 string[] splitOnCase(string str) 270 { 271 import std.ascii : isUpper; 272 import std.uni : toLower; 273 274 string[] res; 275 string tmp; 276 int k = 0; 277 foreach(c; str) { 278 if (c.isUpper && tmp != "") { 279 res ~= tmp; 280 tmp = ""; 281 } 282 tmp ~= c.toLower; 283 k++; 284 } 285 286 foreach(c; str[k..$]) 287 tmp ~= c.toLower; 288 res ~= tmp; 289 290 return res; 291 } 292 293 /// unittest 294 unittest 295 { 296 static assert("fooBarBaz".splitOnCase() == ["foo", "bar", "baz"]); 297 } 298 299 /// Returns true if its a RT function(P) 300 template isSpecifiedCallable(alias D, P, RT) 301 { 302 static if (isCallable!D) 303 enum bool isSpecifiedCallable = (is(ReturnType!D == RT) && (Parameters!D.length > 0) && 304 is(Parameters!D[0] == P)); 305 else 306 enum bool isSpecifiedCallable = false; 307 } 308 309 /// 310 unittest 311 { 312 import std.conv : to; 313 static assert(isSpecifiedCallable!((long ts) => ts.to!string, long, string)); 314 } 315 316 /// Returns true if T has the specified callable 317 template hasSpecifiedCallable(T, string callable, P, RT) 318 { 319 static if (__traits(hasMember, T, callable)) { 320 static if (is(T t : T)) { 321 enum hasSpecifiedCallable = (isSpecifiedCallable!(__traits(getMember, t, callable), P, RT)); 322 } 323 else 324 enum hasSpecifiedCallable = false; 325 } 326 else 327 enum hasSpecifiedCallable = false; 328 } 329 330 unittest 331 { 332 struct A 333 { 334 string foo(string a) 335 { 336 return a; 337 } 338 } 339 340 static assert(hasSpecifiedCallable!(A, "foo", string, string)); 341 static assert(!hasSpecifiedCallable!(A, "foo", int, string)); 342 static assert(!hasSpecifiedCallable!(A, "foo", string, int)); 343 static assert(!hasSpecifiedCallable!(A, "bar", string, string)); 344 } 345 346 /// Find a type matching a criteria or return default 347 template findOrDefault(alias Criteria, Default, T...) { 348 alias Found = Filter!(Criteria, T); 349 350 static if (Found.length == 1) 351 alias findOrDefault = Found[0]; 352 else 353 alias findOrDefault = Default; 354 } 355 356 /// Check if a type matching a criteria exists and only one 357 template onlyOneExists(alias Criteria, T...) { 358 alias Found = Filter!(Criteria, T); 359 360 static if (Found.length == 1) 361 enum onlyOneExists = true; 362 else 363 enum onlyOneExists = false; 364 }