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