diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index c4462466646..022f7eb8e02 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -50,6 +50,7 @@ pub enum CommandKind { Has, Count, Is, + IsMany, Set, } @@ -57,6 +58,7 @@ impl CommandKind { fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool { let count = match self { CommandKind::Has => (1..=3).contains(&args.len()), + CommandKind::IsMany => args.len() >= 3, CommandKind::Count | CommandKind::Is => 3 == args.len(), CommandKind::Set => 4 == args.len(), }; @@ -89,6 +91,7 @@ impl fmt::Display for CommandKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let text = match self { CommandKind::Has => "has", + CommandKind::IsMany => "ismany", CommandKind::Count => "count", CommandKind::Is => "is", CommandKind::Set => "set", @@ -137,6 +140,7 @@ fn get_commands(template: &str) -> Result, ()> { "has" => CommandKind::Has, "count" => CommandKind::Count, "is" => CommandKind::Is, + "ismany" => CommandKind::IsMany, "set" => CommandKind::Set, _ => { print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); @@ -227,6 +231,44 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { _ => unreachable!(), } } + CommandKind::IsMany => { + // @ismany ... + let (path, query, values) = if let [path, query, values @ ..] = &command.args[..] { + (path, query, values) + } else { + unreachable!("Checked in CommandKind::validate") + }; + let val = cache.get_value(path)?; + let got_values = select(&val, &query).unwrap(); + assert!(!command.negated, "`@!ismany` is not supported"); + + // Serde json doesn't implement Ord or Hash for Value, so we must + // use a Vec here. While in theory that makes setwize equality + // O(n^2), in practice n will never be large enought to matter. + let expected_values = + values.iter().map(|v| string_to_value(v, cache)).collect::>(); + if expected_values.len() != got_values.len() { + return Err(CkError::FailedCheck( + format!( + "Expected {} values, but `{}` matched to {} values ({:?})", + expected_values.len(), + query, + got_values.len(), + got_values + ), + command, + )); + }; + for got_value in got_values { + if !expected_values.iter().any(|exp| &**exp == got_value) { + return Err(CkError::FailedCheck( + format!("`{}` has match {:?}, which was not expected", query, got_value), + command, + )); + } + } + true + } CommandKind::Count => { // @count = Check that the jsonpath matches exactly [count] times assert_eq!(command.args.len(), 3);