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