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 (isRandomAccessRange!T) 157 enum isProcessible = true; 158 else static if (is(T == struct) || is(T == union)) 159 { 160 static foreach(member; DrawableMembers!T) 161 static if (!is(typeof(isProcessible) == bool) && 162 !isProcessible!(mixin("T." ~ member))) 163 { 164 enum isProcessible = false; 165 } 166 167 static if (!is(typeof(isProcessible) == bool)) 168 enum isProcessible = true; 169 } 170 else static if ( 171 isStaticArray!T 172 || isAssociativeArray!T 173 || isSomeString!T 174 || isFloatingPoint!T 175 || isIntegral!T 176 || isSomeChar!T 177 || isPointer!T 178 || is(T == bool)) 179 { 180 enum isProcessible = true; 181 } 182 else 183 enum isProcessible = false; 184 } 185 186 private enum isIgnored(alias tested) = __traits(isSame, ignored, tested); 187 188 // This trait defines what members should be drawn - 189 // public members that are either readable and writable or getter properties 190 package template isMemberDrawable(alias value, string member) 191 { 192 import std.algorithm : among; 193 import std.traits : isSomeFunction, isType, Unqual; 194 195 static if (isType!value) 196 alias T = Unqual!value; 197 else 198 alias T = Unqual!(typeof(value)); 199 200 static if (isItSequence!value) 201 enum isMemberDrawable = false; 202 else static if (hasProtection!(value, member) && !isPublic!(value, member)) 203 enum isMemberDrawable = false; 204 else static if (__traits(compiles, { auto v = mixin("T." ~ member); })) // static members 205 enum isMemberDrawable = false; 206 else static if (isItSequence!(__traits(getMember, value, member))) 207 enum isMemberDrawable = false; 208 else static if (isMemberStatic!(T, member)) 209 enum isMemberDrawable = false; 210 else static if (isReadableAndWritable!(value, member) && !isSomeFunction!(__traits(getMember, T, member))) 211 enum isMemberDrawable = !member.among("__ctor", "__dtor"); 212 else static if (isReadable!(value, member)) 213 enum isMemberDrawable = isProperty!(value, member); // a readable property is getter 214 else 215 enum isMemberDrawable = false; 216 } 217 218 template isMemberDrawableAndNotIgnored(alias value, string member) 219 { 220 import std.algorithm : among; 221 import std.meta : AliasSeq, Filter; 222 import std.traits : isSomeFunction, isType, Unqual; 223 224 static if (isType!value) 225 alias T = Unqual!value; 226 else 227 alias T = Unqual!(typeof(value)); 228 229 static if (member.among("__ctor", "__dtor", "this", "~this")) 230 enum isMemberDrawableAndNotIgnored = false; 231 else static if (isSymbol!(__traits(getMember, T, member))) 232 { 233 // check if the symbol has `ignored` attribute assigned 234 static if (Filter!(isIgnored, AliasSeq!(__traits(getAttributes, __traits(getMember, T, member)))).length) 235 enum isMemberDrawableAndNotIgnored = false; 236 else 237 enum isMemberDrawableAndNotIgnored = isMemberDrawable!(value, member); 238 } 239 else 240 enum isMemberDrawableAndNotIgnored = isMemberDrawable!(value, member); 241 } 242 243 /// returns alias sequence, members of which are members of value 244 /// that should be drawn 245 package template DrawableMembers(alias A) 246 { 247 import std.meta : ApplyLeft, Filter, AliasSeq; 248 import std.traits : isType, Unqual; 249 250 static if (isType!A) 251 { 252 alias Type = Unqual!A; 253 } 254 else 255 { 256 alias Type = Unqual!(typeof(A)); 257 } 258 259 Type symbol; 260 261 alias AllMembers = AliasSeq!(__traits(allMembers, Type)); 262 alias isProper = ApplyLeft!(isMemberDrawableAndNotIgnored, symbol); 263 alias DrawableMembers = Filter!(isProper, AllMembers); 264 } 265 266 /* 267 Rendering proxy 268 */ 269 struct renderedAs(T){} 270 struct renderedAsPointee(string N) 271 { 272 enum string name = N; 273 } 274 struct renderedAsMember(string N) 275 { 276 enum string name = N; 277 } 278 struct ignored{} 279 private enum bool isRenderedAs(A) = is(A : renderedAs!T, T); 280 private enum bool isRenderedAs(alias a) = false; 281 package alias getRenderedAs(T : renderedAs!Proxy, Proxy) = Proxy; 282 package template getRenderedAs(alias value) 283 { 284 private alias _list = ProxyList!value; 285 static assert(_list.length == 1, `Only single rendering proxy is allowed`); 286 alias getRenderedAs = _list[0]; 287 } 288 289 private import std.traits : isInstanceOf; 290 private import std.meta : staticMap, Filter; 291 292 private alias ProxyList(alias value) = staticMap!(getRenderedAs, Filter!(isRenderedAs, __traits(getAttributes, value))); 293 private template isSymbol(A...) 294 { 295 static if (A.length == 1) 296 enum isSymbol = .isSymbol!A; 297 else 298 enum isSymbol = false; 299 } 300 private template isSymbol(alias A) 301 { 302 static if (__traits(compiles, { enum id = __traits(identifier, A); })) 303 enum isSymbol = __traits(identifier, A) != "A"; 304 else 305 enum isSymbol = false; 306 } 307 308 package template hasRenderedAs(alias A) 309 { 310 import std.traits : isBuiltinType, isType; 311 static if (isSymbol!A) 312 { 313 private enum listLength = ProxyList!A.length; 314 static assert(listLength <= 1, `Only single rendering proxy is allowed`); 315 enum hasRenderedAs = listLength == 1; 316 } 317 else 318 enum hasRenderedAs = false; 319 } 320 321 private template isRenderedAsMember(alias A) if (isSymbol!A) 322 { 323 enum bool isRenderedAsMember = isInstanceOf!(renderedAsMember, TypeOf!A); 324 } 325 326 private template isRenderedAsMember(alias A) if (!isSymbol!A) 327 { 328 enum bool isRenderedAsMember = false; 329 } 330 331 package template hasRenderedAsMember(T) if (!isSymbol!T) 332 { 333 enum hasRenderedAsMember = false; 334 } 335 336 package template hasRenderedAsMember(T) if (isSymbol!T) 337 { 338 import std.meta : Filter; 339 340 alias attr = Filter!(isRenderedAsMember, __traits(getAttributes, T)); 341 static if (attr.length == 1) 342 enum hasRenderedAsMember = true; 343 else static if (attr.length == 0) 344 enum hasRenderedAsMember = false; 345 else 346 static assert(0, "Only single renderedAsMember attribute is allowd"); 347 } 348 349 package template getRenderedAsMember(T) 350 { 351 alias Attributes = Filter!(isRenderedAsMember, __traits(getAttributes, T)); 352 enum string getRenderedAsMember = Attributes[0].name; 353 } 354 355 alias getRenderedAsMemberString(alias A) = getGivenAttributeAsString!(A, "renderedAsMember"); 356 alias getRenderedAsPointeeString(alias A) = getGivenAttributeAsString!(A, "renderedAsPointee"); 357 358 version(unittest) @Name("getRenderedAsMemberString") 359 unittest 360 { 361 import std.meta : AliasSeq; 362 363 static struct S0 364 { 365 int i; 366 float f; 367 } 368 369 S0 s1; 370 @("renderedAsMember.i") 371 S0 s2; 372 373 static assert ([getRenderedAsMemberString!S0] == []); 374 static assert ([getRenderedAsMemberString!s1] == []); 375 static assert ([getRenderedAsMemberString!s2] == ["i"]); 376 377 static struct S2 378 { 379 S0 s; 380 } 381 382 static struct S3 383 { 384 @("renderedAsMember.s.f") 385 S0 s; 386 } 387 388 static assert ([getRenderedAsMemberString!S2] == []); 389 static assert ([getRenderedAsMemberString!(S3.s)] == ["s.f"]); 390 } 391 392 package template getGivenAttributeAsString(alias A, string GivenAttribute) 393 { 394 import std.meta : staticMap, Filter, AliasSeq; 395 396 enum isString(alias A) = is(TypeOf!A == string); 397 enum L = GivenAttribute.length; 398 enum isGivenAttribute(string s) = s.length > L && s[0..L] == GivenAttribute; 399 enum dropPrefix(string s) = s[L+1..$]; 400 alias T = TypeOf!A; 401 402 template impl(alias N) 403 { 404 alias AllString = Filter!(isString, __traits(getAttributes, N)); 405 alias AllAttrib = Filter!(isGivenAttribute, AllString); 406 alias Attr = staticMap!(dropPrefix, AllAttrib); 407 static assert(Attr.length < 2, "Only single " ~ GivenAttribute ~ " attribute is allowed"); 408 409 static if (Attr.length == 1) 410 alias impl = Attr; 411 else 412 alias impl = AliasSeq!(); 413 } 414 415 static if (isSymbol!A) 416 alias getGivenAttributeAsString = impl!A; 417 else static if (isSymbol!T) 418 alias getGivenAttributeAsString = impl!T; 419 else 420 alias getGivenAttributeAsString = AliasSeq!(); 421 }