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 }