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 provided on dlang forum and on bolts package check them out
37 here: https://forum.dlang.org/post/gdipbdsoqdywuabnpzpe@forum.dlang.org and at
38 code.dlang.org or even https://github.com/aliak00/bolts/ .
39 */
40 
41 /++
42 Extras to object module
43 
44 This file defines extra functions to object module.
45 
46 Authors: Luís Ferreira <luis@aurorafoss.org>
47 Copyright: All rights reserved, Aurora Free Open Source Software
48 License: GNU Lesser General Public License (Version 3, 29 June 2007)
49 Date: 2018-2020
50 +/
51 module aurorafw.stdx.object;
52 
53 import std.traits;
54 import std.meta;
55 
56 import aurorafw.stdx.traits;
57 
58 version (unittest) import aurorafw.unit.assertion;
59 
60 public template match(handlers...)
61 {
62 	MatchReturnType!handlers match(T)(auto ref T obj) if (is(T == class) || is(T == interface))
63 	{
64 		alias TL = staticMap!(Unqual, staticMap!(FunctionTypeOf, handlers));
65 		static assert(is(NoDuplicates!(TL) == TL));
66 
67 		enum CompParams(alias H1, alias H2) = Parameters!H1.length > Parameters!H2.length;
68 		alias matchReturnType = typeof(return);
69 		alias sortedHandlers = AliasSeq!(
70 				staticSort!(CompParams, handlers), // default if everything fails
71 				() => matchReturnType.init);
72 		foreach (handler; sortedHandlers)
73 		{
74 			alias paramsHandler = Parameters!handler;
75 			static assert(paramsHandler.length <= 1);
76 			static if (paramsHandler.length == 1)
77 			{
78 				alias firstParam = Parameters!handler[0];
79 
80 				// runtime verifications
81 				if (typeid(obj) == typeid(firstParam))
82 					return handler(cast(firstParam) obj);
83 			}
84 			else static if (paramsHandler.length == 0)
85 			{
86 				return handler();
87 			}
88 		}
89 	}
90 }
91 
92 private version (unittest)
93 {
94 	// dfmt off
95 	@safe pure
96 	class UnittestFoobar {}
97 
98 	@safe pure
99 	class UnittestFoo : UnittestFoobar {}
100 
101 	@safe pure
102 	class UnittestBar : UnittestFoobar {}
103 	// dfmt on
104 }
105 
106 public enum from = FromImpl!()();
107 
108 public template _from(string moduleName = null)
109 {
110 	enum _from = FromImpl!(moduleName)();
111 }
112 
113 private struct FromImpl(string moduleName = null)
114 {
115 	template opDispatch(string symbolName)
116 	{
117 		static if (ModuleContainsSymbol!(moduleName, symbolName))
118 		{
119 			mixin("import " ~ moduleName ~ ";");
120 			mixin("alias opDispatch = " ~ symbolName ~ ";");
121 		}
122 		else
123 		{
124 			static if (moduleName.length == 0)
125 			{
126 				enum opDispatch = FromImpl!(symbolName)();
127 			}
128 			else
129 			{
130 				enum importString = moduleName ~ "." ~ symbolName;
131 				static assert(
132 						CanImport!importString,
133 						"Symbol \"" ~ symbolName ~ "\" not found in " ~ moduleName
134 				);
135 				enum opDispatch = FromImpl!importString();
136 			}
137 		}
138 	}
139 }
140 
141 ///
142 @safe pure
143 @("Object: from opDispatch sugar")
144 unittest
145 {
146 	assertTrue(__traits(compiles, { _from!"std.math".abs(-1); }));
147 	assertTrue(__traits(compiles, { _from!().std.math.abs(-1); }));
148 	assertTrue(__traits(compiles, { from.std.math.abs(-1); }));
149 	assertTrue(__traits(compiles, { _from!"std.math".abs(-1); }));
150 	assertFalse(__traits(compiles, { from.std.math.thisFunctionDoesNotExist(42); }));
151 	assertFalse(__traits(compiles, { _from!"std.math".thisFunctionDoesNotExist(42); }));
152 
153 	// check for static eval
154 	static assert(__traits(compiles, { _from!"std.math".abs(-1); }));
155 	static assert(__traits(compiles, { from.std.math.abs(-1); }));
156 
157 	// test if it actually imports the right symbol
158 	assertEquals(1, from.std.math.abs(-1));
159 }
160 
161 ///
162 @system
163 @("Object: Type pattern matching")
164 unittest
165 {
166 	UnittestFoobar foo = new UnittestFoo();
167 	assertEquals("yes", foo.match!(
168 			(UnittestFoo _) => "yes", (UnittestBar _) => "bar", () => "no"
169 	));
170 
171 	Object bar = new UnittestBar();
172 	assertEquals("yes", bar.match!(
173 			(UnittestBar _) => "yes", () => "no"
174 	));
175 
176 	assertEquals(bar, bar.match!(
177 			(UnittestBar _) => _, () => null
178 	));
179 
180 	assertEquals(null, bar.match!((UnittestFoo _) => _));
181 }