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