1 ///
2 module drmi.core.base;
3
4 import std.array : appender;
5
6 import drmi.core.types;
7 import drmi.core.exceptions;
8 import drmi.core.helpers;
9
10 ///
11 interface RMICom
12 {
13 ///
14 RMIResponse process(RMICall call);
15 }
16
17 ///
18 class RMISkeleton(T) : RMICom
19 if (is(T == interface))
20 {
21 protected:
22 T server;
23
24 auto sBuffer = appender!(ubyte[]);
25
26 public:
27
28 ///
29 this(T server)
30 {
31 import std.exception : enforce;
32 this.server = enforce(server, "server is null");
33 }
34
35 override RMIResponse process(RMICall call)
36 {
37 import std.meta;
38 import std.traits;
39 import std.typecons : tuple;
40 import std.conv : to;
41
42 template ov(string s) { alias ov = AliasSeq!(__traits(getOverloads, T, s)); }
43 sBuffer.clear();
44
45 switch (call.func)
46 {
47 foreach (n, func; staticMap!(ov, __traits(derivedMembers, T)))
48 {
49 case rmiFunctionName!func:
50 try
51 {
52 auto params = Parameters!func.init;
53
54 static if (params.length)
55 params = call.data.sbinDeserialize!(typeof(tuple(params)));
56
57 enum callstr = "server."~__traits(identifier, func)~"(params)";
58 static if(is(ReturnType!func == void)) mixin(callstr~";");
59 else
60 {
61 auto v = mixin(callstr);
62 sBuffer.sbinSerialize(v);
63 }
64
65 return RMIResponse(0, call, sBuffer.data);
66 }
67 catch (Throwable e)
68 {
69 sBuffer.sbinSerialize(e.to!string);
70 return RMIResponse(2, call, sBuffer.data);
71 }
72 }
73 default:
74 sBuffer.sbinSerialize("unknown func");
75 return RMIResponse(1, call, sBuffer.data);
76 }
77 }
78 }
79
80 ///
81 interface RMIStubCom : RMICom
82 {
83 ///
84 string caller() const @property;
85 }
86
87 ///
88 class RMIStub(T) : T
89 {
90 protected:
91 RMIStubCom com;
92 auto sBuffer = appender!(ubyte[]);
93
94 public:
95 ///
96 this(RMIStubCom com) { this.com = com; }
97
98 private mixin template overrideIface()
99 {
100 private enum string packCode =
101 q{
102 enum dummy;
103 alias self = AliasSeq!(__traits(parent, dummy))[0];
104
105 static fname = rmiFunctionName!self;
106
107 RMICall call;
108 call.caller = com.caller;
109 call.func = fname;
110 call.ts = Clock.currStdTime;
111
112 auto tmp = tuple(Parameters!self.init);
113 foreach (i, p; ParameterIdentifierTuple!self)
114 tmp[i] = mixin(p);
115
116 sBuffer.clear();
117 sBuffer.sbinSerialize(tmp);
118 call.data = sBuffer.data;
119
120 auto result = com.process(call);
121
122 enforce(result.status == 0,
123 new RMIProcessException(result,
124 result.data.sbinDeserialize!string));
125
126 static if (!is(typeof(return) == void))
127 return result.data.sbinDeserialize!(typeof(return));
128 };
129
130 import std.datetime : Clock;
131 import std.exception : enforce;
132 import std.typecons : tuple;
133 import std.meta : staticMap;
134 import std.traits : ReturnType, AliasSeq, Parameters, ParameterIdentifierTuple,
135 functionAttributes, FunctionAttribute;
136
137 private mixin template impl(F...)
138 {
139 private static string trueParameters(alias FNC)()
140 {
141 import std.conv : text;
142 import std..string : join;
143 string[] ret;
144 foreach (i, param; Parameters!FNC)
145 ret ~= `Parameters!(F[0])[`~text(i)~`] __param_` ~ text(i);
146 return ret.join(", ");
147 }
148
149 private static string getAttributesString(alias FNC)()
150 {
151 import std..string : join;
152 string[] ret;
153 // TODO
154 ret ~= functionAttributes!FNC & FunctionAttribute.property ? "@property" : "";
155 return ret.join(" ");
156 }
157
158 static if (F.length == 1)
159 {
160 mixin("override ReturnType!(F[0]) " ~ __traits(identifier, F[0]) ~
161 `(` ~ trueParameters!(F[0]) ~ `) ` ~ getAttributesString!(F[0]) ~
162 ` { ` ~ packCode ~ `}`);
163 }
164 else
165 {
166 mixin impl!(F[0..$/2]);
167 mixin impl!(F[$/2..$]);
168 }
169 }
170
171 private template getOverloads(string s)
172 { alias getOverloads = AliasSeq!(__traits(getOverloads, T, s)); }
173
174 mixin impl!(staticMap!(getOverloads, __traits(derivedMembers, T)));
175 }
176
177 mixin overrideIface;
178 }
179
180 private version (unittest)
181 {
182 struct Point { double x, y, z; }
183
184 interface Test
185 {
186 int foo(string abc, int xyz);
187 string foo(string str);
188 string bar(double val);
189 double len(Point pnt);
190 string state() @property;
191 void state(string s) @property;
192 }
193
194 class Impl : Test
195 {
196 string _state;
197 override:
198 string foo(string str) { return "<" ~ str ~ ">"; }
199 int foo(string abc, int xyz) { return cast(int)(abc.length * xyz); }
200 string bar(double val) { return val > 3.14 ? "big" : "small"; }
201 double len(Point pnt)
202 {
203 import std.math;
204 return sqrt(pnt.x^^2 + pnt.y^^2 + pnt.z^^2);
205 }
206 string state() @property { return _state; }
207 void state(string s) @property { _state = s; }
208 }
209 }
210
211 unittest
212 {
213 auto rea = new Impl;
214 auto ske = new RMISkeleton!Test(rea);
215 auto cli = new RMIStub!Test(new class RMIStubCom
216 {
217 string caller() const @property { return "fake caller"; }
218 RMIResponse process(RMICall call) { return ske.process(call); }
219 });
220
221 assert(rea.foo("hello", 123) == cli.foo("hello", 123));
222 assert(rea.bar(2.71) == cli.bar(2.71));
223 assert(rea.bar(3.1415) == cli.bar(3.1415));
224 assert(rea.foo("okda") == cli.foo("okda"));
225 assert(rea.len(Point(1,2,3)) == cli.len(Point(1,2,3)));
226
227 static str = "foo";
228 cli.state = str;
229 assert(rea.state == str);
230 assert(cli.state == str);
231 }