@@ -50,13 +50,15 @@ pub enum CommandKind {
5050 Has ,
5151 Count ,
5252 Is ,
53+ IsMany ,
5354 Set ,
5455}
5556
5657impl CommandKind {
5758 fn validate ( & self , args : & [ String ] , command_num : usize , lineno : usize ) -> bool {
5859 let count = match self {
5960 CommandKind :: Has => ( 1 ..=3 ) . contains ( & args. len ( ) ) ,
61+ CommandKind :: IsMany => args. len ( ) >= 3 ,
6062 CommandKind :: Count | CommandKind :: Is => 3 == args. len ( ) ,
6163 CommandKind :: Set => 4 == args. len ( ) ,
6264 } ;
@@ -89,6 +91,7 @@ impl fmt::Display for CommandKind {
8991 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
9092 let text = match self {
9193 CommandKind :: Has => "has" ,
94+ CommandKind :: IsMany => "ismany" ,
9295 CommandKind :: Count => "count" ,
9396 CommandKind :: Is => "is" ,
9497 CommandKind :: Set => "set" ,
@@ -137,6 +140,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
137140 "has" => CommandKind :: Has ,
138141 "count" => CommandKind :: Count ,
139142 "is" => CommandKind :: Is ,
143+ "ismany" => CommandKind :: IsMany ,
140144 "set" => CommandKind :: Set ,
141145 _ => {
142146 print_err ( & format ! ( "Unrecognized command name `@{}`" , cmd) , lineno) ;
@@ -227,6 +231,44 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
227231 _ => unreachable ! ( ) ,
228232 }
229233 }
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+ }
230272 CommandKind :: Count => {
231273 // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
232274 assert_eq ! ( command. args. len( ) , 3 ) ;
0 commit comments