@@ -50,13 +50,15 @@ pub enum CommandKind {
50
50
Has ,
51
51
Count ,
52
52
Is ,
53
+ IsMany ,
53
54
Set ,
54
55
}
55
56
56
57
impl CommandKind {
57
58
fn validate ( & self , args : & [ String ] , command_num : usize , lineno : usize ) -> bool {
58
59
let count = match self {
59
60
CommandKind :: Has => ( 1 ..=3 ) . contains ( & args. len ( ) ) ,
61
+ CommandKind :: IsMany => args. len ( ) >= 3 ,
60
62
CommandKind :: Count | CommandKind :: Is => 3 == args. len ( ) ,
61
63
CommandKind :: Set => 4 == args. len ( ) ,
62
64
} ;
@@ -89,6 +91,7 @@ impl fmt::Display for CommandKind {
89
91
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
90
92
let text = match self {
91
93
CommandKind :: Has => "has" ,
94
+ CommandKind :: IsMany => "ismany" ,
92
95
CommandKind :: Count => "count" ,
93
96
CommandKind :: Is => "is" ,
94
97
CommandKind :: Set => "set" ,
@@ -137,6 +140,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
137
140
"has" => CommandKind :: Has ,
138
141
"count" => CommandKind :: Count ,
139
142
"is" => CommandKind :: Is ,
143
+ "ismany" => CommandKind :: IsMany ,
140
144
"set" => CommandKind :: Set ,
141
145
_ => {
142
146
print_err ( & format ! ( "Unrecognized command name `@{}`" , cmd) , lineno) ;
@@ -227,6 +231,44 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
227
231
_ => unreachable ! ( ) ,
228
232
}
229
233
}
234
+ CommandKind :: IsMany => {
235
+ // @ismany <path> <jsonpath> <value>...
236
+ let ( path, query, values) = if let [ path, query, values @ ..] = & command. args [ ..] {
237
+ ( path, query, values)
238
+ } else {
239
+ unreachable ! ( "Checked in CommandKind::validate" )
240
+ } ;
241
+ let val = cache. get_value ( path) ?;
242
+ let got_values = select ( & val, & query) . unwrap ( ) ;
243
+ assert ! ( !command. negated, "`@!ismany` is not supported" ) ;
244
+
245
+ // Serde json doesn't implement Ord or Hash for Value, so we must
246
+ // use a Vec here. While in theory that makes setwize equality
247
+ // O(n^2), in practice n will never be large enought to matter.
248
+ let expected_values =
249
+ values. iter ( ) . map ( |v| string_to_value ( v, cache) ) . collect :: < Vec < _ > > ( ) ;
250
+ if expected_values. len ( ) != got_values. len ( ) {
251
+ return Err ( CkError :: FailedCheck (
252
+ format ! (
253
+ "Expected {} values, but `{}` matched to {} values ({:?})" ,
254
+ expected_values. len( ) ,
255
+ query,
256
+ got_values. len( ) ,
257
+ got_values
258
+ ) ,
259
+ command,
260
+ ) ) ;
261
+ } ;
262
+ for got_value in got_values {
263
+ if !expected_values. iter ( ) . any ( |exp| & * * exp == got_value) {
264
+ return Err ( CkError :: FailedCheck (
265
+ format ! ( "`{}` has match {:?}, which was not expected" , query, got_value) ,
266
+ command,
267
+ ) ) ;
268
+ }
269
+ }
270
+ true
271
+ }
230
272
CommandKind :: Count => {
231
273
// @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
232
274
assert_eq ! ( command. args. len( ) , 3 ) ;
0 commit comments