1 module dmocks.arguments;
2 
3 import dmocks.dynamic;
4 import dmocks.util;
5 import std.algorithm;
6 import std.conv;
7 import std.range;
8 
9 interface ArgumentsMatch
10 {
11     bool matches(Dynamic[] args);
12     string toString();
13     string diffToString(Dynamic[] args);
14 }
15 
16 //TODO: allow richer specification of arguments
17 class StrictArgumentsMatch : ArgumentsMatch
18 {
19     private Dynamic[] _arguments;
20     this(Dynamic[] args)
21     {
22         _arguments = args;
23     }
24 
25     override bool matches(Dynamic[] args)
26     {
27         return _arguments == args;
28     }
29 
30     override string toString()
31     {
32         return _arguments.formatArguments();
33     }
34 
35     override string diffToString(Dynamic[] args)
36     {
37         import std.format : format;
38 
39         if (args.length != _arguments.length)
40         {
41             return toString ~ " " ~ yellow("(length mismatch)");
42         }
43         string[] argInfos;
44         foreach (e; zip(_arguments, args))
45         {
46             string info = e[0].typename ~ " " ~ e[0].toString();
47             if (e[0] != e[1])
48             {
49                 if (e[0].toString() != e[1].toString())
50                 {
51                     info ~= " " ~ yellow(format!"(but got %s)"(e[1]));
52                 }
53                 else
54                 {
55                     info ~= " " ~ yellow("(same toString but unequal)");
56                 }
57             }
58             argInfos ~= info;
59         }
60         return "(" ~ argInfos.join(", ") ~ ")";
61     }
62 }
63 
64 class ArgumentsTypeMatch : ArgumentsMatch
65 {
66     private Dynamic[] _arguments;
67     private bool delegate(Dynamic, Dynamic) _del;
68     this(Dynamic[] args, bool delegate(Dynamic, Dynamic) del)
69     {
70         _arguments = args;
71         _del = del;
72     }
73 
74     override bool matches(Dynamic[] args)
75     {
76         import std.range;
77 
78         if (args.length != _arguments.length)
79             return false;
80 
81         foreach (e; zip(_arguments, args))
82         {
83             if (e[0].type != e[1].type)
84                 return false;
85             if (!_del(e[0], e[1]))
86                 return false;
87         }
88         return true;
89     }
90 
91     override string toString()
92     {
93         return "(" ~ _arguments.map!(a => a.typename).join(", ") ~ ")";
94     }
95 
96     override string diffToString(Dynamic[] args)
97     {
98         import std.format : format;
99 
100         if (args.length != _arguments.length)
101         {
102             return toString ~ " " ~ yellow("(length mismatch)");
103         }
104         string[] argInfos = null;
105         foreach (e; zip(_arguments, args))
106         {
107             string info = e[0].typename;
108             if (e[0].type != e[1].type)
109                 info ~= " " ~ yellow(format!"(but got %s)"(e[1].type));
110             if (!_del(e[0], e[1]))
111             {
112                 if (e[0].toString() != e[1].toString())
113                 {
114                     import dshould.stringcmp : oneLineDiff;
115 
116                     auto diff = oneLineDiff(e[0].toString(), e[1].toString());
117                     info ~= " "
118                         ~ yellow("(but comparator failed. ")
119                         ~ format!"Info: expected.toString %s, got.toString %s"(
120                             diff.original, diff.target)
121                         ~ yellow(")");
122                 }
123                 else
124                 {
125                     info ~= " " ~ yellow("(but comparator failed. Info: expected.toString == got.toString)");
126                 }
127             }
128             argInfos ~= info;
129         }
130         return "(" ~ argInfos.join(", ") ~ ")";
131     }
132 }
133 
134 interface IArguments
135 {
136     string toString();
137     bool opEquals(Object other);
138 }
139 
140 auto arguments(ARGS...)(ARGS args)
141 {
142     Dynamic[] res = new Dynamic[](ARGS.length);
143     foreach (i, arg; args)
144     {
145         res[i] = dynamic(arg);
146     }
147     return res;
148 }
149 
150 auto formatArguments(Dynamic[] _arguments)
151 {
152     return "(" ~ _arguments.map!(a => a.typename ~ " " ~ a.toString()).join(", ") ~ ")";
153 }
154 
155 @("argument equality")
156 unittest
157 {
158     auto a = arguments!(int, real)(5, 9.7);
159     auto b = arguments!(int, real)(5, 9.7);
160     auto c = arguments!(int, real)(9, 1.1);
161     auto d = arguments!(int, float)(5, 9.7f);
162 
163     assert(a == b);
164     assert(a != c);
165     assert(a != d);
166 }
167 
168 @("argument toString")
169 unittest
170 {
171     auto a = arguments!(int, real)(5, 9.7);
172     a.formatArguments();
173 }