json: fix type=const in c++, add failure expectations for non-str const&enum

This commit is contained in:
ochafik 2024-03-17 21:03:48 +00:00
parent 64799baea1
commit 5c50ffaeac
4 changed files with 58 additions and 6 deletions

View file

@ -299,6 +299,10 @@ class SchemaConverter:
self._refs_being_resolved.remove(ref)
return ref_name
def _generate_constant_rule(self, value):
assert isinstance(value, str), f'Only string constants are supported, got {value}'
return self._format_literal(value)
def visit(self, schema, name):
schema_type = schema.get('type')
schema_format = schema.get('format')
@ -314,10 +318,10 @@ class SchemaConverter:
return self._add_rule(rule_name, self._generate_union_rule(name, [{'type': t} for t in schema_type]))
elif 'const' in schema:
return self._add_rule(rule_name, self._format_literal(schema['const']))
return self._add_rule(rule_name, self._generate_constant_rule(schema['const']))
elif 'enum' in schema:
rule = ' | '.join((self._format_literal(v) for v in schema['enum']))
rule = ' | '.join((self._generate_constant_rule(v) for v in schema['enum']))
return self._add_rule(rule_name, rule)
elif schema_type in (None, 'object') and ('properties' in schema or 'additionalProperties' in schema):

View file

@ -547,6 +547,14 @@ public:
visit_refs(schema);
}
string _generate_constant_rule(const json& value) {
if (!value.is_string()) {
_errors.push_back("Only string constants are supported, got " + value.dump());
return "";
}
return _format_literal(value.get<string>());
}
string visit(const json& schema, const string& name) {
json schema_type = schema.contains("type") ? schema["type"] : json();
string schema_format = schema.contains("format") ? schema["format"].get<string>() : "";
@ -564,11 +572,11 @@ public:
}
return _add_rule(rule_name, _generate_union_rule(name, schema_types));
} else if (schema.contains("const")) {
return _add_rule(rule_name, _format_literal(schema["const"].dump()));
return _add_rule(rule_name, _generate_constant_rule(schema["const"]));
} else if (schema.contains("enum")) {
vector<string> enum_values;
for (const auto& v : schema["enum"]) {
enum_values.push_back(_format_literal(v.dump()));
enum_values.push_back(_generate_constant_rule(v));
}
return _add_rule(rule_name, join(enum_values.begin(), enum_values.end(), " | "));
} else if ((schema_type.is_null() || schema_type == "object")

View file

@ -325,6 +325,13 @@ export class SchemaConverter {
return refName;
}
_generateConstantRule(value) {
if (typeof value !== 'string') {
throw new Error('Only string constants are supported, got ' + JSON.stringify(value));
}
return this._formatLiteral(value);
}
visit(schema, name) {
const schemaType = schema.type;
const schemaFormat = schema.format;
@ -338,9 +345,12 @@ export class SchemaConverter {
} else if (Array.isArray(schemaType)) {
return this._addRule(ruleName, this._generateUnionRule(name, schemaType.map(t => ({ type: t }))));
} else if ('const' in schema) {
return this._addRule(ruleName, this._formatLiteral(schema.const));
if (typeof schema.const !== 'string') {
throw new Error('Only string constants are supported, got ' + JSON.stringify(schema.const));
}
return this._addRule(ruleName, this._generateConstantRule(schema.const));
} else if ('enum' in schema) {
const rule = schema.enum.map(v => this._formatLiteral(v)).join(' | ');
const rule = schema.enum.map(v => this._generateConstantRule(v)).join(' | ');
return this._addRule(ruleName, rule);
} else if ((schemaType === undefined || schemaType === 'object') && ('properties' in schema || 'additionalProperties' in schema)) {
const required = new Set(schema.required || []);

View file

@ -155,6 +155,36 @@ static void test_all(const string& lang, std::function<void(const TestCase&)> ru
)"""
});
test({
SUCCESS,
"string const",
R"""({
"const": "foo"
})""",
R"""(
root ::= "\"foo\""
space ::= " "?
)"""
});
test({
FAILURE,
"non-string const",
R"""({
"const": 123
})""",
""
});
test({
FAILURE,
"non-string enum",
R"""({
"enum": [123]
})""",
""
});
test({
SUCCESS,
"tuple1",