1 module dshould.contain;
2 
3 import dshould.ShouldType;
4 import dshould.basic : not, should;
5 
6 /**
7  * The word `.contain` takes one value, expected to appear in the range on the left hand side.
8  */
9 public void contain(Should, T)(Should should, T expected, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
10 if (isInstanceOf!(ShouldType, Should))
11 {
12     should.allowOnlyWords!("not", "only").before!"contain";
13 
14     should.addWord!"contain".checkContain(expected, file, line);
15 }
16 
17 ///
18 unittest
19 {
20     [2, 3, 4].should.contain(3);
21     [2, 3, 4].should.not.contain(5);
22 }
23 
24 public auto contain(Should)(Should should)
25 if (isInstanceOf!(ShouldType, Should))
26 {
27     should.allowOnlyWords!("not").before!"contain";
28 
29     return should.addWord!"contain";
30 }
31 
32 /**
33  * The phrase `.contain.only` or `.only.contain` takes a range, the elements of which are expected to be the only
34  * elements appearing in the range on the left hand side.
35  */
36 public void only(Should, T)(Should should, T expected, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
37 if (isInstanceOf!(ShouldType, Should))
38 {
39     should.requireWord!"contain".before!"only";
40     should.allowOnlyWords!("not", "contain").before!"only";
41 
42     should.addWord!"only".checkContain(expected, file, line);
43 }
44 
45 ///
46 unittest
47 {
48     [3, 4].should.only.contain([4, 3]);
49     [3, 4].should.only.contain([1, 2, 3, 4]);
50     [3, 4].should.contain.only([4, 3]);
51     [2, 3, 4].should.not.only.contain([4, 3]);
52 }
53 
54 public auto only(Should)(Should should)
55 if (isInstanceOf!(ShouldType, Should))
56 {
57     should.allowOnlyWords!("not").before!"only";
58 
59     return should.addWord!"only";
60 }
61 
62 /**
63  * The phrase `.contain.all` takes a range, all elements of which are expected to appear
64  * in the range on the left hand side.
65  */
66 public void all(Should, T)(Should should, T expected, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
67 if (isInstanceOf!(ShouldType, Should))
68 {
69     should.requireWord!"contain".before!"all";
70     should.allowOnlyWords!("not", "contain").before!"all";
71 
72     should.addWord!"all".checkContain(expected, file, line);
73 }
74 
75 ///
76 unittest
77 {
78     [2, 3, 4].should.contain.all([3]);
79     [2, 3, 4].should.contain.all([4, 3]);
80     [2, 3, 4].should.not.contain.all([3, 4, 5]);
81 }
82 
83 /**
84  * The phrase `.contain.any` takes a range, at least one element of which is expected to appear
85  * in the range on the left hand side.
86  */
87 public void any(Should, T)(Should should, T expected, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
88 if (isInstanceOf!(ShouldType, Should))
89 {
90     should.requireWord!"contain".before!"any";
91     should.allowOnlyWords!("not", "contain").before!"any";
92 
93     should.addWord!"any".checkContain(expected, file, line);
94 }
95 
96 ///
97 unittest
98 {
99     [2, 3, 4].should.contain.any([4, 5]);
100     [2, 3, 4].should.not.contain.any([5, 6]);
101 }
102 
103 unittest
104 {
105     const int[] constArray = [2, 3, 4];
106 
107     constArray.should.contain(4);
108 }
109 
110 private void checkContain(Should, T)(Should should, T expected, string file, size_t line)
111 if (isInstanceOf!(ShouldType, Should))
112 {
113     import std.algorithm : any, all, canFind;
114     import std.format : format;
115     import std.range : ElementType, save;
116 
117     with (should)
118     {
119         auto got = should.got();
120 
121         enum rhsIsValue = is(const T == const ElementType!(typeof(got)));
122 
123         static if (rhsIsValue)
124         {
125             allowOnlyWords!("not", "only", "contain").before!"contain";
126 
127             static if (hasWord!"only")
128             {
129                 static if (hasWord!"not")
130                 {
131                     check(
132                         got.any!(a => a != expected),
133                         format("array containing values other than %s", expected),
134                         format("%s", got),
135                         file, line);
136                 }
137                 else
138                 {
139                     check(
140                         got.all!(a => a == expected),
141                         format("array containing only the value %s", expected),
142                         format("%s", got),
143                         file, line);
144                 }
145             }
146             else
147             {
148                 static if (hasWord!"not")
149                 {
150                     check(
151                         !got.save.canFind(expected),
152                         format("array not containing %s", expected),
153                         format("%s", got),
154                         file, line);
155                 }
156                 else
157                 {
158                     check(
159                         got.save.canFind(expected),
160                         format("array containing %s", expected),
161                         format("%s", got),
162                         file, line);
163                 }
164             }
165         }
166         else
167         {
168             static if (hasWord!"only")
169             {
170                 static if (hasWord!"not")
171                 {
172                     check(
173                         !got.all!(a => expected.save.canFind(a)),
174                         format("array containing values other than %s", expected),
175                         format("%s", got),
176                         file, line);
177                 }
178                 else
179                 {
180                     check(
181                         got.all!(a => expected.save.canFind(a)),
182                         format("array containing only the values %s", expected),
183                         format("%s", got),
184                         file, line);
185                 }
186             }
187             else static if (hasWord!"all")
188             {
189                 static if (hasWord!"not")
190                 {
191                     check(
192                         !expected.all!(a => got.save.canFind(a)),
193                         format("array not containing every value in %s", expected),
194                         format("%s", got),
195                         file, line);
196                 }
197                 else
198                 {
199                     check(
200                         expected.all!(a => got.save.canFind(a)),
201                         format("array containing every value in %s", expected),
202                         format("%s", got),
203                         file, line);
204                 }
205             }
206             else static if (hasWord!"any")
207             {
208                 static if (hasWord!"not")
209                 {
210                     check(
211                         !expected.any!(a => got.save.canFind(a)),
212                         format("array not containing any value in %s", expected),
213                         format("%s", got),
214                         file, line);
215                 }
216                 else
217                 {
218                     check(
219                         expected.any!(a => got.save.canFind(a)),
220                         format("array containing any value of %s", expected),
221                         format("%s", got),
222                         file, line);
223                 }
224             }
225             else
226             {
227                 static assert(false,
228                     `bad grammar: expected "contain all", "contain any", "contain only" (or "only contain")`);
229             }
230         }
231     }
232 }