Skip to content

Commit cb81ab3

Browse files
committed
Make sure we correctly support additionalProperties set to True
Fix #150
1 parent 33d6deb commit cb81ab3

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

src/json_schema/parsing.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ impl<'a> Parser<'a> {
4949

5050
#[allow(clippy::wrong_self_convention)]
5151
pub fn to_regex(&mut self, json: &Value) -> Result<String> {
52+
// TODO: the current design of this parser is conceptually limited and should be reworked!
53+
//
54+
// It branch in the following `match` for specific keys in the JSON schema (e.g., "allOf", "anyOf", etc.).
55+
// This approach makes these keys mutually exclusive, meaning that if a JSON schema contains multiple keys
56+
// that should be combined, the parser will just silently take the first one and ignore the others.
57+
//
58+
// To address this, the parser should retain some state while parsing, using a data structure that, e.g.,
59+
// can contain one option per possible key. This would allow the parser to handle more complex cases by
60+
// combining constraints appropriately.
5261
match json {
5362
Value::Object(obj) if obj.is_empty() => self.parse_empty_object(),
5463
Value::Object(obj) if obj.contains_key("properties") => self.parse_properties(obj),
@@ -62,6 +71,15 @@ impl<'a> Parser<'a> {
6271
Value::Object(obj) if obj.contains_key("type") => self.parse_type(obj),
6372
json => Err(Error::UnsupportedJsonSchema(Box::new(json.clone()))),
6473
}
74+
// Each function in this recursive descent parser generates a regex as a `String`, eventually patching
75+
// JSONSchema on the fly and calling `.to_regex()`, which is then combined using simple string formatting.
76+
// This approach is extremely fragile and not expressive. It is prone to errors and difficult to maintain
77+
// or debug.
78+
//
79+
// The right way to implement this is to design the parser like a small compiler with an intermediate
80+
// representation (IR). This IR would be a higher-level representation of regex that can be combined and
81+
// mutated more easily. The code generation step could then be a default `Display` string formatter
82+
// version of the IR. This approach would be more robust, easier to debug, and maintainable.
6583
}
6684

6785
fn parse_empty_object(&mut self) -> Result<String> {
@@ -164,6 +182,15 @@ impl<'a> Parser<'a> {
164182
regex += &format!("({})?", possible_patterns.join("|"));
165183
}
166184

185+
// FIXME: the following lines broke `with_whitespace_patterns`,
186+
// `indirect_recursion_with_recursion_level_regex_match` and
187+
// `direct_recursion_in_array_and_default_behaviour`
188+
if obj.contains_key("type") {
189+
// This is a hack to quickly fix issue #150
190+
let s = self.parse_type(obj)?;
191+
regex += &format!(",{}", &s[2..s.len() - 2]);
192+
}
193+
167194
regex += &format!("{}\\}}", self.whitespace_pattern);
168195
Ok(regex)
169196
}
@@ -503,7 +530,9 @@ impl<'a> Parser<'a> {
503530
let additional_properties = obj.get("additionalProperties");
504531

505532
let value_pattern = match additional_properties {
506-
None | Some(&Value::Bool(true)) => {
533+
// FIXME: the following lines broke `test_unconstrained_others` and `test_schema_matches_regex`
534+
None | Some(&Value::Bool(false)) => "".to_string(),
535+
Some(&Value::Bool(true)) => {
507536
let mut legal_types = vec![
508537
json!({"type": "string"}),
509538
json!({"type": "number"}),

0 commit comments

Comments
 (0)