1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2018-2020 Aurora Free Open Source Software.
10 Copyright (C) 2018-2020 Luís Ferreira <luis@aurorafoss.org>
11 
12 This file is part of the Aurora Free Open Source Software. This
13 organization promote free and open source software that you can
14 redistribute and/or modify under the terms of the GNU Lesser General
15 Public License Version 3 as published by the Free Software Foundation or
16 (at your option) any later version approved by the Aurora Free Open Source
17 Software Organization. The license is available in the package root path
18 as 'LICENSE' file. Please review the following information to ensure the
19 GNU Lesser General Public License version 3 requirements will be met:
20 https://www.gnu.org/licenses/lgpl.html .
21 
22 Alternatively, this file may be used under the terms of the GNU General
23 Public License version 3 or later as published by the Free Software
24 Foundation. Please review the following information to ensure the GNU
25 General Public License requirements will be met:
26 http://www.gnu.org/licenses/gpl-3.0.html.
27 
28 NOTE: All products, services or anything associated to trademarks and
29 service marks used or referenced on this file are the property of their
30 respective companies/owners or its subsidiaries. Other names and brands
31 may be claimed as the property of others.
32 
33 For more info about intellectual property visit: aurorafoss.org or
34 directly send an email to: contact (at) aurorafoss.org .
35 
36 This file has code parts from 'bolts' package.
37 Check out the project here: https://github.com/aliak00/bolts/ .
38 */
39 
40 /++
41 Extras to std.traits
42 
43 This file defines extra functions to std.traits.
44 
45 Authors: Luís Ferreira <luis@aurorafoss.org>
46 Copyright: All rights reserved, Aurora Free Open Source Software
47 License: GNU Lesser General Public License (Version 3, 29 June 2007)
48 Date: 2018-2019
49 +/
50 module aurorafw.stdx.traits;
51 
52 public import std.traits;
53 
54 import std.format;
55 import std.meta;
56 
57 version (unittest) import aurorafw.unit.assertion;
58 
59 /**
60  * Detect version identifiers
61  *
62  * This is a template to check if a version
63  * identifier is available.
64  *
65  * Examples:
66  * --------------------
67  * static if(isVersion!"unittest")
68  * {
69  *      // code with version unittest ...
70  * }
71  * --------------------
72  */
73 template isVersion(string ver)
74 {
75 	mixin(format(q{
76 		version(%s) {
77 			enum isVersion = true;
78 		}
79 		else {
80 			enum isVersion = false;
81 		}
82 	}, ver));
83 }
84 
85 ///
86 @safe pure
87 @("Traits: isVersion")
88 unittest
89 {
90 	assertTrue(isVersion!"unittest");
91 	assertFalse(isVersion!"this_shouldnt_be_a_version_identifier");
92 }
93 
94 /**
95  * Match functions return type
96  *
97  * This template match the return type of the passed functions
98  * and fail if there's more than one type (except typeof(null))
99  */
100 template MatchReturnType(funcs...) if (allSatisfy!(isSomeFunction, funcs))
101 {
102 	alias types = NoDuplicates!(staticMap!(Unqual, staticMap!(ReturnType, funcs)));
103 	static assert(types.length == 1 || (types.length == 2 && is(types[1] == typeof(
104 			null))));
105 
106 	alias MatchReturnType = types[0];
107 }
108 
109 ///
110 @safe pure
111 @("Traits: MatchReturnType")
112 unittest
113 {
114 	alias TL_1 = MatchReturnType!(void delegate(), void function(), void function(
115 			string foo));
116 	assertTrue(is(TL_1 == void));
117 	assertFalse(is(TL_1 == string));
118 
119 	alias TL_2 = MatchReturnType!(string delegate(), string function(), string function(
120 			int foo), () => null);
121 	assertTrue(is(TL_2 == string));
122 	assertTrue(is(MatchReturnType!(() => "atum") == string));
123 }
124 
125 version (unittest)
126 {
127 	private interface IUnittestTestClass
128 	{
129 	}
130 
131 	private class UnittestTestClass
132 	{
133 	}
134 
135 	private struct UnittestTestStruct
136 	{
137 	}
138 }
139 
140 /**
141  * Checks if type is typeof(null)
142  * Params:
143  *   T = type or typeof a variable for null check
144  */
145 template isNullType(alias T)
146 {
147 	enum isNullType = is(typeof(T) == typeof(null));
148 }
149 
150 ///
151 @safe pure
152 @("Traits: isNullType")
153 unittest
154 {
155 	int a;
156 	int* b = null;
157 	UnittestTestStruct c;
158 	void f()
159 	{
160 	}
161 
162 	assertTrue(isNullType!null);
163 
164 	assertFalse(isNullType!a);
165 	assertFalse(isNullType!b);
166 	assertFalse(isNullType!c);
167 	assertFalse(isNullType!f);
168 }
169 
170 /**
171  * Checks if type is null testable
172  *
173  * Params:
174  *   T = type or typeof a variable
175  */
176 template isNullTestable(alias T)
177 {
178 	enum isNullTestable = __traits(compiles, {
179 			if (T.init is null)
180 			{
181 			}
182 		});
183 }
184 
185 ///
186 @safe pure
187 @("Traits: isNullTestable")
188 unittest
189 {
190 	class Class1
191 	{
192 	}
193 
194 	struct Struct1
195 	{
196 		void opAssign(int*)
197 		{
198 		}
199 	}
200 
201 	assertFalse(isNullTestable!Struct1);
202 	assertTrue(isNullTestable!Class1);
203 	assertTrue(isNullTestable!(int*));
204 
205 	assertFalse(isNullTestable!UnittestTestStruct);
206 	assertFalse(isNullTestable!int);
207 
208 	class Class2
209 	{
210 		@disable this();
211 	}
212 
213 	assertTrue(isNullTestable!Class2);
214 }
215 
216 /**
217  * Checks if type is settable to null
218  * Params:
219  *   T = type or typeof a variable
220  */
221 template isNullSettable(alias T)
222 {
223 	enum isNullSettable = __traits(compiles, { typeof(T.init) t = T.init; t = null; });
224 }
225 
226 ///
227 @safe pure
228 @("Traits: isNullSettable")
229 unittest
230 {
231 	assertTrue(isNullSettable!IUnittestTestClass);
232 	assertTrue(isNullSettable!UnittestTestClass);
233 	assertTrue(isNullSettable!(int[]));
234 	assertTrue(isNullSettable!(int[string]));
235 	assertTrue(isNullSettable!(typeof(null)));
236 	assertTrue(isNullSettable!(int*));
237 	assertTrue(isNullSettable!(void function()));
238 	assertTrue(isNullSettable!(void delegate()));
239 
240 	assertFalse(isNullSettable!int);
241 	assertFalse(isNullSettable!float);
242 	assertFalse(isNullSettable!double);
243 	assertFalse(isNullSettable!bool);
244 	assertFalse(isNullSettable!real);
245 	assertFalse(isNullSettable!UnittestTestStruct);
246 
247 	struct Struct1
248 	{
249 		void opAssign(int*)
250 		{
251 		}
252 	}
253 
254 	assertTrue(isNullSettable!Struct1);
255 
256 	struct Struct3
257 	{
258 		@disable this();
259 		void opAssign(int*)
260 		{
261 		}
262 	}
263 
264 	assertTrue(isNullSettable!Struct3);
265 }
266 
267 /**
268  * Get's an AliasSeq of types from the given arguments
269  */
270 template TypesOf(Symbols...)
271 {
272 	import std.meta : AliasSeq;
273 
274 	static if (Symbols.length)
275 	{
276 		static if (isFunction!(Symbols[0]) && is(typeof(&Symbols[0]) F))
277 		{
278 			alias T = F;
279 		}
280 		else static if (is(typeof(Symbols[0])))
281 		{
282 			alias T = typeof(Symbols[0]);
283 		}
284 		else
285 		{
286 			alias T = Symbols[0];
287 		}
288 		alias TypesOf = AliasSeq!(T, TypesOf!(Symbols[1 .. $]));
289 	}
290 	else
291 	{
292 		alias TypesOf = AliasSeq!();
293 	}
294 }
295 
296 ///
297 @safe pure
298 @("Traits: TypesOf")
299 unittest
300 {
301 	assertTrue(is(TypesOf!("foo", 42U, 24, 123.0, float) == AliasSeq!(string, uint, int, double, float)));
302 	assertTrue(is(TypesOf!(null) == AliasSeq!(typeof(null))));
303 	assertTrue(is(TypesOf!("foobar") == AliasSeq!(string)));
304 
305 	// check for static eval
306 	static assert(is(TypesOf!("foobar") == AliasSeq!(string)));
307 }
308 
309 /**
310  * Trait to verify if a module can be imported
311  *
312  * Params:
313  *   moduleName = module to validate import
314  */
315 template CanImport(string moduleName)
316 {
317 	enum CanImport = __traits(compiles, { mixin("import " ~ moduleName ~ ";"); });
318 }
319 
320 ///
321 @safe pure
322 @("Traits: CanImport")
323 unittest
324 {
325 	assertTrue(CanImport!"std.stdio");
326 	// check for static eval
327 	static assert(CanImport!"std.stdio");
328 
329 	assertFalse(CanImport!"this.module.doesnt.exists.dont.create.it");
330 }
331 
332 /**
333  * Check if module contains a symbol
334  *
335  * Params:
336  *   moduleName = module to be imported
337  *   symbolName = symbol to be checked
338  */
339 template ModuleContainsSymbol(string moduleName, string symbolName)
340 {
341 	enum ModuleContainsSymbol = CanImport!moduleName && __traits(compiles, {
342 			mixin("import " ~ moduleName ~ ":" ~ symbolName ~ ";");
343 		});
344 }
345 
346 ///
347 @safe pure
348 @("Traits: ModuleContainsSymbol")
349 unittest
350 {
351 	assertTrue(ModuleContainsSymbol!("std.stdio", "writeln"));
352 	assertFalse(ModuleContainsSymbol!("std.stdio", "thissymboldoesntactuallyexist"));
353 
354 	// check for static eval
355 	static assert(ModuleContainsSymbol!("std.stdio", "writeln"));
356 }
357 
358 /**
359  * Checks if a certain literal or function return type is equal.
360  * Basically the same as is(... == ...) but resolves the function
361  * return type.
362  */
363 template isOf(ab...) if (ab.length == 2)
364 {
365 	alias Ts = TypesOf!ab;
366 	template resolve(T)
367 	{
368 		import std.traits : isCallable, ReturnType;
369 
370 		static if (isCallable!T)
371 		{
372 			alias resolve = ReturnType!T;
373 		}
374 		else
375 		{
376 			alias resolve = T;
377 		}
378 	}
379 
380 	enum isOf = is(resolve!(Ts[0]) == resolve!(Ts[1]));
381 }
382 
383 ///
384 @safe pure
385 @("Traits: isOf")
386 unittest
387 {
388 	assertTrue(isOf!(int, 3));
389 	assertTrue(isOf!(7, 3));
390 	assertTrue(isOf!(3, int));
391 	assertFalse(isOf!(float, 3));
392 	assertFalse(isOf!(float, string));
393 	assertFalse(isOf!(string, 3));
394 
395 	string foobar()
396 	{
397 		return "";
398 	}
399 
400 	assertTrue(isOf!(string, foobar));
401 	// cover foobar()
402 	assertEquals("", foobar());
403 
404 	// check static eval
405 	static assert(isOf!(string, foobar));
406 }
407 
408 /**
409  * Checks if two symbols are the same in compile-time
410  *
411  * Params:
412  *   lhs = left-hand symbol to compare
413  *   rhs = right-hand symbol to compare
414  */
415 template isSame(alias lhs, alias rhs)
416 {
417 
418 	// this should test if the type is bool easily with
419 	// compiles trait
420 	private static template expectBool(bool b)
421 	{
422 	}
423 
424 	// check if its a type or a value
425 	static if (!__traits(compiles, typeof(lhs)) && !__traits(compiles, typeof(rhs)))
426 	{
427 		enum isSame = is(lhs == rhs);
428 		// check if its comparable
429 	}
430 	else static if (__traits(compiles, typeof(lhs))
431 			&& __traits(compiles, typeof(rhs))
432 			&& __traits(compiles, expectBool!(lhs == rhs))
433 		)
434 	{
435 		// check if its a rvalue
436 		static if (!__traits(compiles, &lhs) || !__traits(compiles, &rhs))
437 
438 			enum isSame = (lhs == rhs);
439 		else // literal comparable
440 			enum isSame = __traits(isSame, lhs, rhs);
441 	}
442 	else
443 	{
444 		// if none of this, fallback to isSame trait
445 		enum isSame = __traits(isSame, lhs, rhs);
446 	}
447 }
448 
449 ///
450 @safe pure
451 @("Traits: isSame")
452 unittest
453 {
454 	assertTrue(isSame!(int, int));
455 	assertFalse(isSame!(int, short));
456 
457 	enum a = 1, b = 1, c = 2, s = "a", t = "a";
458 	assertTrue(isSame!(1, 1));
459 	assertTrue(isSame!(a, 1));
460 	assertTrue(isSame!(a, b));
461 	assertFalse(isSame!(b, c));
462 	assertTrue(isSame!("a", "a"));
463 	assertTrue(isSame!(s, "a"));
464 	assertTrue(isSame!(s, t));
465 	assertFalse(isSame!(s, "g"));
466 	assertFalse(isSame!(1, "1"));
467 	assertFalse(isSame!(a, "a"));
468 	assertTrue(isSame!(isSame, isSame));
469 	assertFalse(isSame!(isSame, a));
470 
471 	assertFalse(isSame!(byte, a));
472 	assertFalse(isSame!(short, isSame));
473 	assertFalse(isSame!(a, int));
474 	assertFalse(isSame!(long, isSame));
475 
476 	static immutable X = 1, Y = 1, Z = 2;
477 	assertTrue(isSame!(X, X));
478 	assertFalse(isSame!(X, Y));
479 	assertFalse(isSame!(Y, Z));
480 
481 	int foo();
482 	int bar();
483 	real baz(int);
484 	assertTrue(isSame!(foo, foo));
485 	assertFalse(isSame!(foo, bar));
486 	assertFalse(isSame!(bar, baz));
487 	assertTrue(isSame!(baz, baz));
488 	assertFalse(isSame!(foo, 0));
489 
490 	int x, y;
491 	real z;
492 	assertTrue(isSame!(x, x));
493 	assertFalse(isSame!(x, y));
494 	assertFalse(isSame!(y, z));
495 	assertTrue(isSame!(z, z));
496 	assertFalse(isSame!(x, 0));
497 }