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