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 private void checkContain(Should, T)(Should should, T expected, string file, size_t line)
104 if (isInstanceOf!(ShouldType, Should))
105 {
106     import std.algorithm : any, all, canFind;
107     import std.range : ElementType, save;
108 
109     with (should)
110     {
111         auto got = should.got();
112 
113         enum rhsIsValue = is(T == ElementType!(typeof(got)));
114 
115         static if (rhsIsValue)
116         {
117             allowOnlyWords!("not", "only", "contain").before!"contain";
118 
119             static if (hasWord!"only")
120             {
121                 static if (hasWord!"not")
122                 {
123                     mixin(genCheck!("%s.any!(a => a != %s)", ["got", "expected"]));
124                 }
125                 else
126                 {
127                     mixin(genCheck!("%s.all!(a => a == %s)", ["got", "expected"]));
128                 }
129             }
130             else
131             {
132                 static if (hasWord!"not")
133                 {
134                     mixin(genCheck!("!%s.save.canFind(%s)", ["got", "expected"]));
135                 }
136                 else
137                 {
138                     mixin(genCheck!("%s.save.canFind(%s)", ["got", "expected"]));
139                 }
140             }
141         }
142         else
143         {
144             static if (hasWord!"only")
145             {
146                 static if (hasWord!"not")
147                 {
148                     mixin(genCheck!("!%s.all!(a => %s.save.canFind(a))", ["got", "expected"]));
149                 }
150                 else
151                 {
152                     mixin(genCheck!("%s.all!(a => %s.save.canFind(a))", ["got", "expected"]));
153                 }
154             }
155             else static if (hasWord!"all")
156             {
157                 static if (hasWord!"not")
158                 {
159                     mixin(genCheck!("!%s.all!(a => %s.save.canFind(a))", ["expected", "got"]));
160                 }
161                 else
162                 {
163                     mixin(genCheck!("%s.all!(a => %s.save.canFind(a))", ["expected", "got"]));
164                 }
165             }
166             else static if (hasWord!"any")
167             {
168                 static if (hasWord!"not")
169                 {
170                     mixin(genCheck!("!%s.any!(a => %s.save.canFind(a))", ["expected", "got"]));
171                 }
172                 else
173                 {
174                     mixin(genCheck!("%s.any!(a => %s.save.canFind(a))", ["expected", "got"]));
175                 }
176             }
177             else
178             {
179                 static assert(false,
180                     `bad grammar: expected "contain all", "contain any", "contain only" (or "only contain")`);
181             }
182         }
183     }
184 }