1 module dshould.thrown;
2 
3 import std.format : format;
4 import std.traits : CommonType;
5 import std.typecons;
6 import dshould.ShouldType;
7 
8 unittest
9 {
10     import dshould.basic : be, equal, not, should;
11 
12     auto error = new Exception("");
13 
14     /**
15      * Throws: Exception
16      */
17     void throwsException() { throw error; }
18 
19     throwsException.should.throwAn!Exception.where.it.should.be(error);
20     throwsException.should.throwAn!Exception.where.it.should.not.be(null);
21 
22     2.should.be(5).because("it just should, okay")
23         .should.throwA!FluentException.where.its.reason.should.equal("it just should, okay");
24 
25     2.should.be(5).should.throwA!FluentException;
26     2.should.be(5).should.throwAn!Error.should.throwA!FluentException;
27 
28     2.should.be(2).should.not.throwA!FluentException;
29 
30     2.should.be(5).should.throwA!FluentException("test");
31 }
32 
33 template throwA(T : Throwable)
34 {
35     auto throwA(Should)(Should should, string file = __FILE__, size_t line = __LINE__)
36     if (isInstanceOf!(ShouldType, Should))
37     {
38         should.allowOnlyWordsBefore!(["not"], "throwA");
39 
40         should.terminateChain;
41 
42         FluentException innerError = null;
43 
44         auto inner()
45         {
46             try
47             {
48                 should.data.lhs();
49             }
50             catch (T throwable)
51             {
52                 static if (should.hasWord!"not")
53                 {
54                     innerError = new FluentException(
55                         format!`expected no %s`(T.stringof),
56                         format!`, but expression threw %s`(throwable),
57                         file, line
58                     );
59                 }
60                 else
61                 {
62                     return throwable;
63                 }
64             }
65 
66             static if (!should.hasWord!"not")
67             {
68                 return null;
69             }
70         }
71 
72         try
73         {
74             static if (is(typeof(inner()) == void))
75             {
76                 inner;
77             }
78             else
79             {
80                 if (auto throwable = inner())
81                 {
82                     return tuple!"where"(tuple!("it", "its")(throwable, throwable));
83                 }
84             }
85         }
86         // don't go up beyond Exception unless we're not from beneath it:
87         // keeps us from needlessly breaking purity.
88         catch (CommonType!(Exception, T) otherThrowable)
89         {
90             static if (should.hasWord!"not")
91             {
92                 return;
93             }
94             else
95             {
96                 throw new FluentException(
97                     format!`expected %s`(T.stringof),
98                     format!`, but expression threw %s`(otherThrowable),
99                     file, line
100                 );
101             }
102         }
103 
104         static if (should.hasWord!"not")
105         {
106             if (innerError !is null)
107             {
108                 throw innerError;
109             }
110         }
111         else
112         {
113             throw new FluentException(
114                 format!`expected %s`(T.stringof),
115                 `, but expression did not throw.`,
116                 file, line
117             );
118         }
119     }
120 }
121 
122 alias throwAn = throwA;
123 
124 T because(T)(lazy T value, string reason)
125 {
126     try
127     {
128         return value;
129     }
130     catch (FluentException fluentException)
131     {
132         throw fluentException.because(reason);
133     }
134 }