1 module dmocks.action; 2 3 import dmocks.dynamic; 4 import dmocks.util; 5 import std.traits; 6 7 package: 8 9 enum ActionStatus 10 { 11 Success, 12 FailBadAction, 13 } 14 15 struct ReturnOrPass(T) 16 { 17 static if (!is(T == void)) 18 { 19 static if (is(typeof({ Unqual!T value; }))) 20 { 21 Unqual!T value; 22 } 23 else 24 { 25 auto value = Unqual!T.init; 26 } 27 } 28 29 bool pass; 30 ActionStatus status = ActionStatus.Success; 31 } 32 33 struct Actor 34 { 35 Action self; 36 37 ReturnOrPass!(TReturn) act (TReturn, ArgTypes...) (ArgTypes args) 38 { 39 debugLog("Actor:act"); 40 41 ReturnOrPass!(TReturn) rope; 42 if (self.passThrough) 43 { 44 rope.pass = true; 45 return rope; 46 } 47 if (self.toThrow) 48 { 49 throw self.toThrow; 50 } 51 static if (is (TReturn == void)) 52 { 53 if (self.action !is null) 54 { 55 debugLog("action found, type: %s", self.action.type); 56 57 if (self.action.type == typeid(void delegate(ArgTypes))) 58 { 59 self.action.get!(void delegate(ArgTypes))()(args); 60 } 61 else 62 { 63 rope.status = ActionStatus.FailBadAction; 64 } 65 } 66 } 67 else 68 { 69 if (self.returnValue !is null) 70 { 71 rope.value = self.returnValue.get!(Unqual!TReturn); 72 } 73 else if (self.action !is null) 74 { 75 debugLog("action found, type: %s", self.action.type); 76 if (self.action.type == typeid(TReturn delegate(ArgTypes))) 77 { 78 rope.value = self.action.get!(Unqual!TReturn delegate(ArgTypes))()(args); 79 } 80 else 81 { 82 rope.status = ActionStatus.FailBadAction; 83 } 84 } 85 } 86 87 return rope; 88 } 89 } 90 91 //TODO: make action parameters orthogonal or disallow certain combinations of them 92 class Action 93 { 94 bool passThrough; 95 96 private Dynamic _returnValue; 97 98 Dynamic action; 99 100 Exception toThrow; 101 102 private TypeInfo _returnType; 103 104 this(TypeInfo returnType) 105 { 106 this._returnType = returnType; 107 } 108 109 bool hasAction() 110 { 111 return (_returnType is typeid(void)) || (passThrough) || 112 (_returnValue !is null) || (action !is null) || (toThrow !is null); 113 } 114 115 Actor getActor() 116 { 117 Actor actor; 118 119 actor.self = this; 120 return actor; 121 } 122 123 @property Dynamic returnValue() 124 { 125 return this._returnValue; 126 } 127 128 @property void returnValue(Dynamic dynamic) 129 { 130 import std.exception : enforce; 131 import std.format : format; 132 133 enforce( 134 dynamic.canConvertTo(this._returnType), 135 format!"Cannot set return value to '%s': expected '%s'"(dynamic.type, this._returnType)); 136 137 this._returnValue = dynamic; 138 } 139 } 140 141 @("action returnValue") 142 unittest 143 { 144 Dynamic v = dynamic(5); 145 Action act = new Action(typeid(int)); 146 assert (act.returnValue is null); 147 act.returnValue = v; 148 assert (act.returnValue == dynamic(5)); 149 } 150 151 @("action throws on mismatching returnValue") 152 unittest 153 { 154 Dynamic v = dynamic(5.0f); 155 Action act = new Action(typeid(int)); 156 bool errored = false; 157 158 assert (act.returnValue is null); 159 160 try 161 { 162 act.returnValue = v; 163 } 164 catch (Exception) 165 { 166 errored = true; 167 } 168 169 assert (errored); 170 } 171 172 @("action action") 173 unittest 174 { 175 Dynamic v = dynamic(5); 176 Action act = new Action(typeid(int)); 177 assert (act.action is null); 178 act.action = v; 179 assert (act.action == v); 180 } 181 182 @("action exception") 183 unittest 184 { 185 Exception ex = new Exception("boogah"); 186 Action act = new Action(typeid(int)); 187 assert (act.toThrow is null); 188 act.toThrow = ex; 189 assert (act.toThrow is ex); 190 } 191 192 @("action passthrough") 193 unittest 194 { 195 Action act = new Action(typeid(int)); 196 act.passThrough = true; 197 assert (act.passThrough); 198 act.passThrough = false; 199 assert (!act.passThrough); 200 } 201 202 @("action hasAction") 203 unittest 204 { 205 Action act = new Action(typeid(int)); 206 act.returnValue = dynamic(5); 207 assert(act.hasAction); 208 }