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 }