From 2b13e40b9194d6da1546c566cfc1fd13f63ee24c Mon Sep 17 00:00:00 2001 From: David Phillips Date: Wed, 9 Apr 2025 17:48:11 +0100 Subject: [PATCH 1/3] Add array and object to enum Need to allow for whitespace --- src/json_schema/mod.rs | 16 ++++++++++++++-- src/json_schema/parsing.rs | 8 ++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/json_schema/mod.rs b/src/json_schema/mod.rs index bbedddcc..202c9e7f 100644 --- a/src/json_schema/mod.rs +++ b/src/json_schema/mod.rs @@ -569,10 +569,22 @@ mod tests { r#"(0|1)"#, vec!["0", "1"], vec!["a"], ), + // Enum array + ( + r#"{"title": "Foo", "enum": [[1,2],[3,4]], "type": "array"}"#, + r#"(\[1,2\]|\[3,4\])"#, + vec!["[1,2]", "[3,4]"], vec!["1", "[1,3]"], + ), + // Enum object + ( + r#"{"title": "Foo", "enum": [{"a":"b"}, {"c":"d"}], "type": "object"}"#, + r#"(\{"a":"b"\}|\{"c":"d"\})"#, + vec![r#"{"a":"b"}"#, r#"{"c":"d"}"#], vec!["a", r#"{"a":"c"}"#], + ), // Enum mix of types ( - r#"{"title": "Foo", "enum": [6, 5.3, "potato", true, null]}"#, - r#"(6|5\.3|"potato"|true|null)"#, + r#"{"title": "Foo", "enum": [6, 5.3, "potato", true, null, [1,2], {"a":"b"}]}"#, + r#"(6|5\.3|"potato"|true|null|\[1,2\]|\{"a":"b"\})"#, vec!["6", "5.3", r#""potato""#, "true", "null"], vec!["none", "53"], ), // ========================================================== diff --git a/src/json_schema/parsing.rs b/src/json_schema/parsing.rs index d6ff8e9f..5caaa7cb 100644 --- a/src/json_schema/parsing.rs +++ b/src/json_schema/parsing.rs @@ -238,11 +238,15 @@ impl<'a> Parser<'a> { let choices: Result> = enum_values .iter() .map(|choice| match choice { - Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => { + Value::Null + | Value::Bool(_) + | Value::Number(_) + | Value::String(_) + | Value::Array(_) + | Value::Object(_) => { let json_string = serde_json::to_string(choice)?; Ok(regex::escape(&json_string)) } - _ => Err(Error::UnsupportedEnumDataType(Box::new(choice.clone()))), }) .collect(); From 5ada0f6dded61bb4ea7ebbf55eb6a877b7219587 Mon Sep 17 00:00:00 2001 From: David Phillips Date: Thu, 10 Apr 2025 22:14:56 +0100 Subject: [PATCH 2/3] parse objects and arrays in const, and support whitespace --- src/json_schema/mod.rs | 12 ++++---- src/json_schema/parsing.rs | 59 ++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/json_schema/mod.rs b/src/json_schema/mod.rs index 202c9e7f..29828da8 100644 --- a/src/json_schema/mod.rs +++ b/src/json_schema/mod.rs @@ -572,19 +572,19 @@ mod tests { // Enum array ( r#"{"title": "Foo", "enum": [[1,2],[3,4]], "type": "array"}"#, - r#"(\[1,2\]|\[3,4\])"#, - vec!["[1,2]", "[3,4]"], vec!["1", "[1,3]"], + format!(r#"(\[{0}1{0},{0}2{0}\]|\[{0}3{0},{0}4{0}\])"#, WHITESPACE).as_str(), + vec!["[1,2]", "[3,4]", "[1, 2 ]"], vec!["1", "[1,3]"], ), // Enum object ( - r#"{"title": "Foo", "enum": [{"a":"b"}, {"c":"d"}], "type": "object"}"#, - r#"(\{"a":"b"\}|\{"c":"d"\})"#, - vec![r#"{"a":"b"}"#, r#"{"c":"d"}"#], vec!["a", r#"{"a":"c"}"#], + r#"{"title": "Foo", "enum": [{"a":"b","c":"d"}, {"e":"f"}], "type": "object"}"#, + format!(r#"(\{{{0}"a"{0}:{0}"b"{0},{0}"c"{0}:{0}"d"{0}\}}|\{{{0}"e"{0}:{0}"f"{0}\}})"#, WHITESPACE).as_str(), + vec![r#"{"a":"b","c":"d"}"#, r#"{"e":"f"}"#, r#"{"a" : "b", "c": "d" }"#], vec!["a", r#"{"a":"b"}"#], ), // Enum mix of types ( r#"{"title": "Foo", "enum": [6, 5.3, "potato", true, null, [1,2], {"a":"b"}]}"#, - r#"(6|5\.3|"potato"|true|null|\[1,2\]|\{"a":"b"\})"#, + format!(r#"(6|5\.3|"potato"|true|null|\[{0}1{0},{0}2{0}\]|\{{{0}"a"{0}:{0}"b"{0}\}})"#, WHITESPACE).as_str(), vec!["6", "5.3", r#""potato""#, "true", "null"], vec!["none", "53"], ), // ========================================================== diff --git a/src/json_schema/parsing.rs b/src/json_schema/parsing.rs index 5caaa7cb..8eaa5bd6 100644 --- a/src/json_schema/parsing.rs +++ b/src/json_schema/parsing.rs @@ -237,17 +237,7 @@ impl<'a> Parser<'a> { Some(Value::Array(enum_values)) => { let choices: Result> = enum_values .iter() - .map(|choice| match choice { - Value::Null - | Value::Bool(_) - | Value::Number(_) - | Value::String(_) - | Value::Array(_) - | Value::Object(_) => { - let json_string = serde_json::to_string(choice)?; - Ok(regex::escape(&json_string)) - } - }) + .map(|choice| self.parse_const_value(choice)) .collect(); let choices = choices?; @@ -258,17 +248,42 @@ impl<'a> Parser<'a> { } fn parse_const(&mut self, obj: &serde_json::Map) -> Result { - match obj.get("const") { - Some(const_value) => match const_value { - Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => { - let json_string = serde_json::to_string(const_value)?; - Ok(regex::escape(&json_string)) - } - _ => Err(Error::UnsupportedConstDataType(Box::new( - const_value.clone(), - ))), - }, - None => Err(Error::ConstKeyNotFound), + if let Some(const_value) = obj.get("const") { + self.parse_const_value(const_value) + } else { + Err(Error::ConstKeyNotFound) + } + } + + fn parse_const_value(&self, value: &Value) -> Result { + match value { + Value::Array(array_values) => { + let inner_regex = array_values + .iter() + .map(|value| self.parse_const_value(value)) + .collect::>>()? + .join(format!("{0},{0}", self.whitespace_pattern).as_str()); + Ok(format!(r"\[{0}{inner_regex}{0}\]", self.whitespace_pattern)) + } + Value::Object(obj) => { + let inner_regex = obj + .iter() + .map(|(key, value)| { + self.parse_const_value(value).map(|value_regex| { + format!(r#""{key}"{0}:{0}{value_regex}"#, self.whitespace_pattern) + }) + }) + .collect::>>()? + .join(format!("{0},{0}", self.whitespace_pattern).as_str()); + Ok(format!( + r"\{{{0}{inner_regex}{0}\}}", + self.whitespace_pattern + )) + } + _ => { + let json_string = serde_json::to_string(value)?; + Ok(regex::escape(&json_string)) + } } } From b9782bc42764e883430f7ae644cdffa1c952066a Mon Sep 17 00:00:00 2001 From: David Phillips Date: Thu, 10 Apr 2025 22:46:17 +0100 Subject: [PATCH 3/3] Add extra checks to test case --- src/json_schema/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json_schema/mod.rs b/src/json_schema/mod.rs index 29828da8..1516cf2d 100644 --- a/src/json_schema/mod.rs +++ b/src/json_schema/mod.rs @@ -585,7 +585,7 @@ mod tests { ( r#"{"title": "Foo", "enum": [6, 5.3, "potato", true, null, [1,2], {"a":"b"}]}"#, format!(r#"(6|5\.3|"potato"|true|null|\[{0}1{0},{0}2{0}\]|\{{{0}"a"{0}:{0}"b"{0}\}})"#, WHITESPACE).as_str(), - vec!["6", "5.3", r#""potato""#, "true", "null"], vec!["none", "53"], + vec!["6", "5.3", r#""potato""#, "true", "null", "[1, 2]", r#"{"a": "b" }"#], vec!["none", "53"], ), // ========================================================== // UUID