1 module dc;
2 
3 import std.algorithm.comparison : predSwitch;
4 import std.conv : to;
5 import std.string : strip;
6 
7 /// https://dlang.org/blog/2017/02/13/a-new-import-idiom/
8 template from(alias name) {
9 	mixin("import from = " ~ name ~ ";");
10 }
11 
12 unittest {
13 	static assert(__traits(compiles, from!"std".writeln()));
14 	static assert(__traits(compiles, from!"std.stdio".readln()));
15 }
16 
17 /// Enum that contains compile date in YYYYMMDD format
18 enum compileDate =
19 	__DATE__[7 .. $].to!int * 10000
20 	+ __DATE__[0 .. 3].predSwitch(
21 		"Jan", 1,
22 		"Feb", 2,
23 		"Mar", 3,
24 		"Apr", 4,
25 		"May", 5,
26 		"Jun", 6,
27 		"Jul", 7,
28 		"Aug", 8,
29 		"Sep", 9,
30 		"Oct", 10,
31 		"Nov", 11,
32 		"Dec", 12
33 	) * 100
34 	+ __DATE__[4 .. 6].strip.to!int;
35 
36 unittest {
37 	import std.math : log10;
38 	static assert(cast(int) log10(compileDate) == 7, "Date \"" ~ compileDate.to!string ~ "\" isn't 8 numbers long");
39 	enum month = compileDate / 100 % 100;
40 	static assert(month >= 1 && month <= 12, "Invalid month: " ~ month.to!string);
41 	enum day = compileDate % 100;
42 	static assert(day >= 1 && day <= 31, "Invalid day: " ~ day.to!string);
43 }
44 
45 /// Template that calls `static assert` if compile date is past of what was passed as an argument. Go figure out what to do with that.
46 template bestBefore(int deprecatedStamp) {
47 	static assert(compileDate < deprecatedStamp);
48 }
49 
50 unittest {
51 	static assert(__traits(compiles, {
52 		@bestBefore!99991231
53 		struct A { }
54 	}));
55 	static assert(!__traits(compiles, {
56 		@bestBefore!19700101
57 		struct A { }
58 	}));
59 }
60 
61 /++
62 $(P Struct that provides following things:)
63 1. RAII wrapper above handle type
64 2. Mechanism that strips prefix from names of functions in given module. Those functions are available through `opDispatch`-based mechanism.
65 
66 Params:
67 	module_		= module with functions from which given prefix should be stripped
68 	prefix		= prefix that should be stripped from names of functions
69 	constructor	= a function that creates a handle. Note: constructor must take at least one non-optional argument
70 	destructor	= function that gets called when struct gets destroyed. Mustn't take any arguments
71 
72 Examples:
73 ---
74 import etc.c.curl;
75 alias Curl = CWrap!(etc.c.curl, "curl_easy_", (int _) => curl_easy_init(), curl_easy_cleanup);
76 auto c = Curl(0);
77 c.setopt(CurlOption.url, "https://dlang.org");
78 c.perform();
79 ---
80 +/
81 struct CWrap(alias module_, string prefix, alias constructor, alias destructor) {
82 	import std.traits : Parameters, ReturnType;
83 
84 	ReturnType!constructor handle;
85 	alias handle this;
86 
87 	@disable this();
88 	@disable this(ref typeof(this));
89 	this(Parameters!constructor args) {
90 		handle = constructor(args);
91 	}
92 
93 	~this() {
94 		destructor(handle);
95 	}
96 
97 	template opDispatch(string f) {
98 		alias fun = __traits(getMember, module_, prefix ~ f);
99 		alias returnType = ReturnType!fun;
100 		returnType opDispatch(Args...)(Args args) {
101 			static if(is(returnType == void)) {
102 				fun(handle, args);
103 			} else {
104 				return fun(handle, args);
105 			}
106 		}
107 	}
108 }
109 
110 unittest {
111 	bool didDestroy = false;
112 	struct Foo_ {
113 		int a;
114 	}
115 	Foo_* foo_new(int a) {
116 		return new Foo_(a);
117 	}
118 	void foo_del(Foo_* h) @trusted {
119 		h.a = 0;
120 		from!"core.memory".GC.free(h);
121 		didDestroy = true;
122 	}
123 	int foo_get_a(Foo_* h) {
124 		return h.a;
125 	}
126 	void foo_set_a(Foo_* h, int a) {
127 		h.a = a;
128 	}
129 	struct mod {
130 		alias foo__new = foo_new;
131 		alias foo__del = foo_del;
132 		alias foo__get_a = foo_get_a;
133 		alias foo__set_a = foo_set_a;
134 	}
135 	alias Foo = CWrap!(mod, "foo__", foo_new, foo_del);
136 	{
137 		auto foo = Foo(3);
138 		assert(foo.get_a == 3);
139 		foo.set_a(4);
140 		assert(foo.get_a == 4);
141 	}
142 	assert(didDestroy);
143 }
144 
145 /++
146 A copy of std::tie from C++.
147 TODO: write proper documentation for this
148 Examples:
149 ---
150 int a;
151 string b;
152 unwrap(a, b) = tuple(3, "hi");
153 assert(a == 3 && b == hi);
154 ---
155 +/
156 auto unwrap(T...)(out T args) {
157 	struct UnwrapImpl {
158 		void opAssign(U)(U tuple) if(U.length == T.length) {
159 			static foreach(i; 0 .. U.length) {
160 				args[i] = tuple[i];
161 			}
162 		}
163 	}
164 
165 	return UnwrapImpl();
166 }
167 
168 unittest {
169 	int a;
170 	string b;
171 	double[] c;
172 	unwrap(a, b, c) = from!"std.typecons".tuple(1, "b", [1.0, 4.3]);
173 	assert(a == 1);
174 	assert(b == "b");
175 	assert(c == [1.0, 4.3]);
176 }
177 
178 /++
179 Template that generates sort of infix functions.
180 To use generated function, surround it with any of overloadable binary operators.
181 
182 adr thinks this is disgusting.
183 
184 Params:
185 	fun	= function that takes two arguments, lhs and rhs respectively
186 
187 Examples:
188 ---
189 enum w = Infix!((a, b) => a + b);
190 enum map = Infix!((a, b) => from!"std".map!(a => a.b)(a));
191 assert(2 ~w~ 3 == 5);
192 assert([1, 2, 3] <<map>> ((int a) => a * a));
193 ---
194 +/
195 template Infix(alias fun) {
196 	struct Impl {
197 		auto opBinaryRight(string op, T)(T lhs) {
198 			struct Other {
199 				auto opBinary(string other, U)(U rhs) {
200 					return fun(lhs, rhs);
201 				}
202 			}
203 			return Other();
204 		}
205 	}
206 	enum Infix = Impl();
207 }
208 
209 unittest {
210 	import std : array, stdMap = map;
211 	enum w = Infix!((a, b) => a + b);
212 	enum map = Infix!((a, b) => (a.stdMap!(a => b(a)).array));
213 	assert(2 ~w~ 3 == 5);
214 	assert([1, 2, 3] <<map>> ((int a) => a * a) == [1, 4, 9]);
215 }