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 }