1 module tagion.testbench.actor.util;
2 
3 import core.time;
4 import std.variant;
5 import tagion.actor.exceptions;
6 import tagion.utils.pretend_safe_concurrency : MessageMismatch, receiveTimeout;
7 
8 private string format(Args...)(Args args) @trusted {
9     import f = std.format;
10 
11     return f.format(args);
12 }
13 
14 /// Exception sent when the actor gets a message that it doesn't handle
15 @safe class MessageTimeout : ActorException {
16     this(immutable(char)[] msg, string file = __FILE__, size_t line = __LINE__) pure {
17         super(msg, file, line);
18     }
19 }
20 
21 private template receiveOnlyRet(T...) {
22     static if (T.length == 1) {
23         alias receiveOnlyRet = T[0];
24     }
25     else {
26         import std.typecons : Tuple;
27 
28         alias receiveOnlyRet = Tuple!(T);
29     }
30 }
31 
32 /**
33 * Throws: MessageTimeout, if no message is received before Duration
34 * Throws: WrongMessage, if incorrect message was received
35 */
36 T receiveOnlyTimeout(T)(Duration d = 1.seconds) @safe {
37     T ret;
38     const received = receiveTimeout(
39             d,
40             (T val) { ret = val; },
41             (Variant val) @trusted {
42         throw new MessageMismatch(
43             format("Unexpected message got \n\t%s \nof type \n\t%s \nexpected %s", val, val.type.toString(), T
44             .stringof));
45     }
46     );
47 
48     if (!received) {
49         throw new MessageTimeout(
50                 format(
51                 "Timed out never received message expected message type: \n\t%s".format(T.stringof))
52         );
53     }
54 
55     return ret;
56 }
57 
58 import std.typecons : Tuple;
59 
60 /// ditto
61 Tuple!(T) receiveOnlyTimeout(T...)(Duration d = 1.seconds) @safe if (T.length > 1) {
62     import std.meta : allSatisfy;
63     import std.traits : isAssignable;
64 
65     Tuple!(T) ret;
66     const received = receiveTimeout(
67             d,
68             (T val) @safe {
69         static if (allSatisfy!(isAssignable, T)) {
70             ret.field = val;
71         }
72         else {
73             import core.lifetime : emplace;
74 
75             emplace(&ret, val);
76         }
77     },
78             (Variant val) @trusted {
79         throw new MessageMismatch(
80             format("Unexpected message got %s of type %s, expected %s", val, val.type.toString(), T
81             .stringof));
82     }
83     );
84 
85     if (!received) {
86         throw new MessageTimeout(
87                 format(
88                 "Timed out never received message expected message type: %s".format(T.stringof))
89         );
90     }
91 
92     return ret;
93 }