1 module dorm.lib.ffi_wrap;
2 
3 // public import dorm.lib.ffi_impl;
4 
5 import std.array;
6 import std.conv;
7 import std.stdio;
8 import std.traits;
9 import std.typecons : tuple;
10 
11 private static import dorm.lib.ffi_impl;
12 
13 private string formatArgs(T...)(T args)
14 {
15 	auto ret = appender!string;
16 	bool first = true;
17 	static foreach (arg; args)
18 	{
19 		if (!first)
20 			ret ~= ", ";
21 		first = false;
22 		ret ~= "\n\t";
23 		ret ~= formatArg(arg);
24 	}
25 	static if (args.length > 0)
26 		ret ~= "\n";
27 	return ret.data;
28 }
29 
30 private string formatArg(T)(T value)
31 {
32 	static if (is(T : const(FFICondition)*) || is(T : FFICondition*))
33 	{
34 		return value ? formatArg(*value) : "(no condition)";
35 	}
36 	else static if (is(T == enum))
37 	{
38 		static if (is(OriginalType!T == U*, U))
39 			return T.stringof ~ "@" ~ (cast(size_t)value).to!string(16);
40 		else
41 			return T.stringof ~ "." ~ value.to!string;
42 	}
43 	else static if (is(T == U*, U))
44 		return U.stringof ~ "@" ~ (cast(size_t)value).to!string(16);
45 	else
46 	{
47 		return value.to!string;
48 	}
49 }
50 
51 size_t dormTraceFun(T...)(bool withRet, string method, T args)
52 {
53 	static __gshared size_t id;
54 	if (withRet)
55 	{
56 		auto reqId = id++;
57 		stderr.writeln("[trace] #", reqId, " ", method, " (", formatArgs(args), ")");
58 		return reqId;
59 	}
60 	else
61 	{
62 		stderr.writeln("[trace] (void) ", method, "(", formatArgs(args), ")");
63 		return 0;
64 	}
65 }
66 
67 void dormTraceRetval(T)(size_t reqid, T retval)
68 {
69 	stderr.writeln("[trace] #", reqid, " -> returned ", retval);
70 }
71 
72 void dormTraceCallback(scope dorm.lib.ffi_impl.RormError error)
73 {
74 	if (error)
75 		stderr.writeln("[trace] Callback (failed) -> ", error.makeException.msg);
76 	else
77 		stderr.writeln("[trace] Callback success");
78 }
79 
80 void dormTraceCallback(T)(scope T result, scope dorm.lib.ffi_impl.RormError error)
81 {
82 	if (error)
83 		stderr.writeln("[trace] Callback (failed) -> ", error.makeException.msg);
84 	else
85 		stderr.writeln("[trace] Callback success: ", formatArg(result));
86 }
87 
88 void dormTraceSyncCallback(scope dorm.lib.ffi_impl.RormError error)
89 {
90 	if (error)
91 		stderr.writeln("[trace] Sync Callback (failed) -> ", error.makeException.msg);
92 	else
93 		stderr.writeln("[trace] Sync Callback success");
94 }
95 
96 void dormTraceSyncCallback(T)(scope T result, scope dorm.lib.ffi_impl.RormError error)
97 {
98 	if (error)
99 		stderr.writeln("[trace] Sync Callback (failed) -> ", error.makeException.msg);
100 	else
101 		stderr.writeln("[trace] Sync Callback success: ", formatArg(result));
102 }
103 
104 // ---------------------------------------------------------------------------------
105 
106 private static string generateTracer(string symbolName)()
107 {
108 	alias symbol = __traits(getMember, dorm.lib.ffi_impl, symbolName);
109 
110 	static if (is(ReturnType!symbol == void))
111 	{
112 		return "extern(D) " ~ ReturnType!symbol.stringof
113 			~ " " ~ symbolName ~ "_wrapper(Parameters!(dorm.lib.ffi_impl." ~ symbolName ~ ")) {
114 				import dorm.lib.ffi_impl : " ~ symbolName ~ ";
115 				dormTraceFun(false, `" ~ symbolName ~ "`, __traits(parameters));
116 				" ~ symbolName ~ "(__traits(parameters));
117 			}
118 			alias " ~ symbolName ~ " = " ~ symbolName ~ "_wrapper;";
119 	}
120 	else
121 	{
122 		return "extern(D) " ~ ReturnType!symbol.stringof
123 			~ " " ~ symbolName ~ "_wrapper(Parameters!(dorm.lib.ffi_impl." ~ symbolName ~ ")) {
124 				import dorm.lib.ffi_impl : " ~ symbolName ~ ";
125 				auto reqid = dormTraceFun(true, `" ~ symbolName ~ "`, __traits(parameters));
126 
127 				auto retval = " ~ symbolName ~ "(__traits(parameters));
128 				dormTraceRetval(reqid, retval);
129 				return retval;
130 			}
131 			alias " ~ symbolName ~ " = " ~ symbolName ~ "_wrapper;";
132 	}
133 }
134 
135 static foreach (symbol; __traits(allMembers, dorm.lib.ffi_impl))
136 {
137 	static if (__traits(compiles, mixin("dorm.lib.ffi_impl.", symbol)))
138 	{
139 		static if (is(typeof(__traits(getMember, dorm.lib.ffi_impl, symbol)) == function)
140 			&& __traits(getLinkage, __traits(getMember, dorm.lib.ffi_impl, symbol)) == "C")
141 		{
142 			mixin(generateTracer!symbol);
143 		}
144 		else static if (symbol != "object" && symbol != "dorm")
145 			mixin("alias ", symbol , " = dorm.lib.ffi_impl.", symbol , ";");
146 	}
147 }
148