1 module auxil.traits; 2 3 version(unittest) import unit_threaded : Name; 4 5 import std.traits : isTypeTuple; 6 7 template isNullable(T) 8 { 9 import std.traits : isInstanceOf; 10 import std.typecons : Nullable; 11 12 enum isNullable = is(T == struct) && isInstanceOf!(Nullable, T); 13 } 14 15 template isTimemarked(T) 16 { 17 version (HAVE_TIMEMARKED) 18 { 19 import std.traits : isInstanceOf; 20 import rdp.timemarked : Timemarked; 21 22 enum isTimemarked = is(T == struct) && isInstanceOf!(Timemarked, T); 23 } 24 else 25 enum isTimemarked = false; 26 } 27 28 template hasRenderHeader(alias A) 29 { 30 import auxil.model : FixedAppender; 31 32 FixedAppender!32 app; 33 34 static if (is(typeof(A.renderHeader(app)))) 35 enum hasRenderHeader = true; 36 else 37 enum hasRenderHeader = false; 38 } 39 40 private bool privateOrPackage()(string protection) 41 { 42 return protection == "private" || protection == "package"; 43 } 44 45 template TypeOf(alias A) 46 { 47 import std.traits : isType, Unqual; 48 49 static if (isType!A) 50 { 51 alias TypeOf = Unqual!A; 52 } 53 else 54 { 55 alias TypeOf = Unqual!(typeof(A)); 56 } 57 } 58 59 // check if the member is readable/writeble? 60 private enum isReadableAndWritable(alias aggregate, string member) = __traits(compiles, __traits(getMember, aggregate, member) = __traits(getMember, aggregate, member)); 61 private enum isPublic(alias aggregate, string member) = !__traits(getProtection, __traits(getMember, aggregate, member)).privateOrPackage; 62 63 // check if the member is property with const qualifier 64 private template isConstProperty(alias aggregate, string member) 65 { 66 import std.traits : isSomeFunction, hasFunctionAttributes; 67 68 static if(isSomeFunction!(__traits(getMember, typeof(aggregate), member))) 69 enum isConstProperty = hasFunctionAttributes!(__traits(getMember, aggregate, member), "const", "@property"); 70 else 71 enum isConstProperty = false; 72 } 73 74 // check if the member is property 75 private template isProperty(alias aggregate, string member) 76 { 77 import std.traits : isSomeFunction, functionAttributes, FunctionAttribute; 78 79 static if(isSomeFunction!(__traits(getMember, typeof(aggregate), member))) 80 enum isProperty = !!(functionAttributes!(__traits(getMember, typeof(aggregate), member)) & FunctionAttribute.property); 81 else 82 enum isProperty = false; 83 } 84 85 // check if the member is readable 86 private enum isReadable(alias aggregate, string member) = __traits(compiles, { auto _val = __traits(getMember, aggregate, member); }); 87 private enum isMemberStatic(T, string member) = __traits(compiles, { auto a = mixin("T." ~ member); }); 88 89 private template isItSequence(T...) 90 { 91 static if (T.length < 2) 92 enum isItSequence = false; 93 else 94 enum isItSequence = true; 95 } 96 97 private template hasProtection(alias aggregate, string member) 98 { 99 enum hasProtection = __traits(compiles, { enum pl = __traits(getProtection, __traits(getMember, typeof(aggregate), member)); }); 100 } 101 102 version(unittest) @Name("aggregates") 103 @safe 104 unittest 105 { 106 import unit_threaded : should, be; 107 108 static struct Test 109 { 110 float f = 7.7; 111 int i = 8; 112 string s = "some text"; 113 } 114 115 static struct StructWithStruct 116 { 117 double d = 8.8; 118 long l = 999; 119 Test t; 120 } 121 122 static class TestClass 123 { 124 125 } 126 127 static struct StructWithPointerAndClass 128 { 129 double* d; 130 TestClass tc; 131 } 132 133 static struct StructWithNestedClass 134 { 135 TestClass tc; 136 } 137 138 assert( isProcessible!float); 139 assert( isProcessible!(float*)); 140 assert( isProcessible!Test ); 141 assert( isProcessible!StructWithStruct); 142 assert(!isProcessible!TestClass); 143 assert(!isProcessible!StructWithPointerAndClass); 144 assert(!isProcessible!StructWithNestedClass); 145 } 146 147 template isProcessible(alias A) 148 { 149 import std.traits, std.range; 150 151 static if (isType!A) 152 alias T = Unqual!A; 153 else 154 alias T = Unqual!(typeof(A)); 155 156 static if (is(T == struct) || is(T == union)) 157 { 158 static foreach(member; DrawableMembers!T) 159 static if (!is(typeof(isProcessible) == bool) && 160 !isProcessible!(mixin("T." ~ member))) 161 { 162 enum isProcessible = false; 163 } 164 165 static if (!is(typeof(isProcessible) == bool)) 166 enum isProcessible = true; 167 } 168 else static if ( 169 isStaticArray!T 170 || isRandomAccessRange!T 171 || isAssociativeArray!T 172 || isSomeString!T 173 || isFloatingPoint!T 174 || isIntegral!T 175 || isSomeChar!T 176 || isPointer!T 177 || is(T == bool)) 178 { 179 enum isProcessible = true; 180 } 181 else 182 enum isProcessible = false; 183 } 184 185 private enum isIgnored(alias tested) = __traits(isSame, ignored, tested); 186 187 // This trait defines what members should be drawn - 188 // public members that are either readable and writable or getter properties 189 package template isMemberDrawable(alias value, string member) 190 { 191 import std.algorithm : among; 192 import std.traits : isSomeFunction, isType, Unqual; 193 194 static if (isType!value) 195 alias T = Unqual!value; 196 else 197 alias T = Unqual!(typeof(value)); 198 199 static if (isItSequence!value) 200 enum isMemberDrawable = false; 201 else static if (hasProtection!(value, member) && !isPublic!(value, member)) 202 enum isMemberDrawable = false; 203 else static if (__traits(compiles, { auto v = mixin("T." ~ member); })) // static members 204 enum isMemberDrawable = false; 205 else static if (isItSequence!(__traits(getMember, value, member))) 206 enum isMemberDrawable = false; 207 else static if (isMemberStatic!(T, member)) 208 enum isMemberDrawable = false; 209 else static if (isReadableAndWritable!(value, member) && !isSomeFunction!(__traits(getMember, T, member))) 210 enum isMemberDrawable = !member.among("__ctor", "__dtor"); 211 else static if (isReadable!(value, member)) 212 enum isMemberDrawable = isProperty!(value, member); // a readable property is getter 213 else 214 enum isMemberDrawable = false; 215 } 216 217 template isMemberDrawableAndNotIgnored(alias value, string member) 218 { 219 import std.algorithm : among; 220 import std.meta : AliasSeq, Filter; 221 import std.traits : isSomeFunction, isType, Unqual; 222 223 static if (isType!value) 224 alias T = Unqual!value; 225 else 226 alias T = Unqual!(typeof(value)); 227 228 static if (member.among("__ctor", "__dtor", "this", "~this")) 229 enum isMemberDrawableAndNotIgnored = false; 230 else static if (isSymbol!(__traits(getMember, T, member))) 231 { 232 // check if the symbol has `ignored` attribute assigned 233 static if (Filter!(isIgnored, AliasSeq!(__traits(getAttributes, __traits(getMember, T, member)))).length) 234 enum isMemberDrawableAndNotIgnored = false; 235 else 236 enum isMemberDrawableAndNotIgnored = isMemberDrawable!(value, member); 237 } 238 else 239 enum isMemberDrawableAndNotIgnored = isMemberDrawable!(value, member); 240 } 241 242 /// returns alias sequence, members of which are members of value 243 /// that should be drawn 244 package template DrawableMembers(alias A) 245 { 246 import std.meta : ApplyLeft, Filter, AliasSeq; 247 import std.traits : isType, Unqual; 248 249 static if (isType!A) 250 { 251 alias Type = Unqual!A; 252 } 253 else 254 { 255 alias Type = Unqual!(typeof(A)); 256 } 257 258 Type symbol; 259 260 alias AllMembers = AliasSeq!(__traits(allMembers, Type)); 261 alias isProper = ApplyLeft!(isMemberDrawableAndNotIgnored, symbol); 262 alias DrawableMembers = Filter!(isProper, AllMembers); 263 } 264 265 /* 266 Rendering proxy 267 */ 268 struct renderedAs(T){} 269 struct renderedAsPointee(string N) 270 { 271 enum string name = N; 272 } 273 struct renderedAsMember(string N) 274 { 275 enum string name = N; 276 } 277 struct ignored{} 278 private enum bool isRenderedAs(A) = is(A : renderedAs!T, T); 279 private enum bool isRenderedAs(alias a) = false; 280 package alias getRenderedAs(T : renderedAs!Proxy, Proxy) = Proxy; 281 package template getRenderedAs(alias value) 282 { 283 private alias _list = ProxyList!value; 284 static assert(_list.length == 1, `Only single rendering proxy is allowed`); 285 alias getRenderedAs = _list[0]; 286 } 287 288 private import std.traits : isInstanceOf; 289 private import std.meta : staticMap, Filter; 290 291 private alias ProxyList(alias value) = staticMap!(getRenderedAs, Filter!(isRenderedAs, __traits(getAttributes, value))); 292 private template isSymbol(A...) 293 { 294 static if (A.length == 1) 295 enum isSymbol = .isSymbol!A; 296 else 297 enum isSymbol = false; 298 } 299 private template isSymbol(alias A) 300 { 301 static if (__traits(compiles, { enum id = __traits(identifier, A); })) 302 enum isSymbol = __traits(identifier, A) != "A"; 303 else 304 enum isSymbol = false; 305 } 306 307 package template hasRenderedAs(alias A) 308 { 309 import std.traits : isBuiltinType, isType; 310 static if (isSymbol!A) 311 { 312 private enum listLength = ProxyList!A.length; 313 static assert(listLength <= 1, `Only single rendering proxy is allowed`); 314 enum hasRenderedAs = listLength == 1; 315 } 316 else 317 enum hasRenderedAs = false; 318 } 319 320 private template isRenderedAsMember(alias A) if (isSymbol!A) 321 { 322 enum bool isRenderedAsMember = isInstanceOf!(renderedAsMember, TypeOf!A); 323 } 324 325 private template isRenderedAsMember(alias A) if (!isSymbol!A) 326 { 327 enum bool isRenderedAsMember = false; 328 } 329 330 package template hasRenderedAsMember(T) if (!isSymbol!T) 331 { 332 enum hasRenderedAsMember = false; 333 } 334 335 package template hasRenderedAsMember(T) if (isSymbol!T) 336 { 337 import std.meta : Filter; 338 339 alias attr = Filter!(isRenderedAsMember, __traits(getAttributes, T)); 340 static if (attr.length == 1) 341 enum hasRenderedAsMember = true; 342 else static if (attr.length == 0) 343 enum hasRenderedAsMember = false; 344 else 345 static assert(0, "Only single renderedAsMember attribute is allowd"); 346 } 347 348 package template getRenderedAsMember(T) 349 { 350 alias Attributes = Filter!(isRenderedAsMember, __traits(getAttributes, T)); 351 enum string getRenderedAsMember = Attributes[0].name; 352 } 353 354 alias getRenderedAsMemberString(alias A) = getGivenAttributeAsString!(A, "renderedAsMember"); 355 alias getRenderedAsPointeeString(alias A) = getGivenAttributeAsString!(A, "renderedAsPointee"); 356 357 version(unittest) @Name("getRenderedAsMemberString") 358 unittest 359 { 360 import std.meta : AliasSeq; 361 362 static struct S0 363 { 364 int i; 365 float f; 366 } 367 368 S0 s1; 369 @("renderedAsMember.i") 370 S0 s2; 371 372 static assert ([getRenderedAsMemberString!S0] == []); 373 static assert ([getRenderedAsMemberString!s1] == []); 374 static assert ([getRenderedAsMemberString!s2] == ["i"]); 375 376 static struct S2 377 { 378 S0 s; 379 } 380 381 static struct S3 382 { 383 @("renderedAsMember.s.f") 384 S0 s; 385 } 386 387 static assert ([getRenderedAsMemberString!S2] == []); 388 static assert ([getRenderedAsMemberString!(S3.s)] == ["s.f"]); 389 } 390 391 package template getGivenAttributeAsString(alias A, string GivenAttribute) 392 { 393 import std.meta : staticMap, Filter, AliasSeq; 394 395 enum isString(alias A) = is(TypeOf!A == string); 396 enum L = GivenAttribute.length; 397 enum isGivenAttribute(string s) = s.length > L && s[0..L] == GivenAttribute; 398 enum dropPrefix(string s) = s[L+1..$]; 399 alias T = TypeOf!A; 400 401 template impl(alias N) 402 { 403 alias AllString = Filter!(isString, __traits(getAttributes, N)); 404 alias AllAttrib = Filter!(isGivenAttribute, AllString); 405 alias Attr = staticMap!(dropPrefix, AllAttrib); 406 static assert(Attr.length < 2, "Only single " ~ GivenAttribute ~ " attribute is allowed"); 407 408 static if (Attr.length == 1) 409 alias impl = Attr; 410 else 411 alias impl = AliasSeq!(); 412 } 413 414 static if (isSymbol!A) 415 alias getGivenAttributeAsString = impl!A; 416 else static if (isSymbol!T) 417 alias getGivenAttributeAsString = impl!T; 418 else 419 alias getGivenAttributeAsString = AliasSeq!(); 420 }