Skip to content

Commit 63d57e3

Browse files
committed
Simplify const/enum
1 parent 0a80648 commit 63d57e3

5 files changed

Lines changed: 64 additions & 77 deletions

File tree

src/error-handlers/constAndEnum.js

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental";
44
import jsonStringify from "json-stringify-deterministic";
55

66
/**
7-
* @import { ErrorHandler, ErrorObject, Json } from "../index.d.ts"
7+
* @import { ErrorHandler, Json } from "../index.d.ts"
88
*/
99

1010
/**
@@ -16,83 +16,63 @@ import jsonStringify from "json-stringify-deterministic";
1616

1717
/** @type {ErrorHandler} */
1818
const constAndEnumErrorHandler = async (normalizedErrors, instance, localization) => {
19-
/** @type {ErrorObject[]} */
20-
const errors = [];
19+
/** @type Set<string> | undefined */
20+
let allowedJson;
2121

22-
/** @type {Constraint[]} */
23-
const constraints = [];
24-
let hasFailure = false;
22+
/** @type string[]> */
23+
const constSchemaLocations = [];
24+
25+
/** @type string[]> */
26+
const enumSchemaLocations = [];
27+
28+
/** @type string[]> */
29+
const allSchemaLocations = [];
2530

2631
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/const"]) {
27-
const passed = normalizedErrors["https://json-schema.org/keyword/const"][schemaLocation] === true;
28-
if (!passed) {
29-
hasFailure = true;
32+
if (!normalizedErrors["https://json-schema.org/keyword/const"][schemaLocation]) {
33+
constSchemaLocations.push(schemaLocation);
3034
}
35+
allSchemaLocations.push(schemaLocation);
36+
3137
const keyword = await getSchema(schemaLocation);
32-
constraints.push({
33-
allowedValues: [Schema.value(keyword)],
34-
schemaLocation
35-
});
38+
const keywordJson = new Set([jsonStringify(/** @type Json */ (Schema.value(keyword)))]);
39+
40+
allowedJson = allowedJson?.intersection(keywordJson) ?? keywordJson;
3641
}
3742

3843
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/enum"]) {
39-
const passed = normalizedErrors["https://json-schema.org/keyword/enum"][schemaLocation] === true;
40-
if (!passed) {
41-
hasFailure = true;
44+
if (!normalizedErrors["https://json-schema.org/keyword/enum"][schemaLocation]) {
45+
enumSchemaLocations.push(schemaLocation);
4246
}
47+
allSchemaLocations.push(schemaLocation);
48+
4349
const keyword = await getSchema(schemaLocation);
44-
constraints.push({
45-
allowedValues: Schema.value(keyword),
46-
schemaLocation
47-
});
48-
}
50+
const keywordJson = new Set(/** @type Json[] */ (Schema.value(keyword)).map((value) => jsonStringify(value)));
4951

50-
if (!hasFailure || constraints.length === 0) {
51-
return errors;
52+
allowedJson = allowedJson?.intersection(keywordJson) ?? keywordJson;
5253
}
5354

54-
let intersectionKeys = new Set(constraints[0].allowedValues.map(toKey));
55-
for (let i = 1; i < constraints.length; i++) {
56-
intersectionKeys = intersectionKeys.intersection(new Set(constraints[i].allowedValues.map(toKey)));
55+
if (constSchemaLocations.length === 0 && enumSchemaLocations.length === 0) {
56+
return [];
5757
}
5858

59-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
60-
const intersection = /** @type {Json[]} */ ([...intersectionKeys].map((k) => JSON.parse(k)));
61-
62-
const instanceLocation = Instance.uri(instance);
63-
const intersectionKeysArray = [...intersectionKeys];
64-
const exactMatch = constraints.find((c) => {
65-
if (c.allowedValues.length !== intersection.length) return false;
66-
const constraintKeys = new Set(c.allowedValues.map(toKey));
67-
return intersectionKeysArray.every((k) => constraintKeys.has(k));
68-
});
69-
70-
if (intersection.length === 0) {
71-
errors.push({
59+
if (allowedJson?.size === 0) {
60+
return [{
7261
message: localization.getBooleanSchemaErrorMessage(),
73-
instanceLocation,
74-
schemaLocations: constraints.map((c) => c.schemaLocation)
75-
});
76-
} else if (intersection.length === 1) {
77-
errors.push({
78-
message: localization.getConstErrorMessage(intersection[0]),
79-
instanceLocation,
80-
schemaLocations: exactMatch ? [exactMatch.schemaLocation] : constraints.map((c) => c.schemaLocation)
81-
});
62+
instanceLocation: Instance.uri(instance),
63+
schemaLocations: allSchemaLocations
64+
}];
8265
} else {
83-
errors.push({
84-
message: localization.getEnumErrorMessage(intersection),
85-
instanceLocation,
86-
schemaLocations: exactMatch ? [exactMatch.schemaLocation] : constraints.map((c) => c.schemaLocation)
87-
});
66+
/** @type Json[] */
67+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
68+
const allowedValues = [...allowedJson ?? []].map((json) => JSON.parse(json));
69+
70+
return [{
71+
message: localization.getEnumErrorMessage(allowedValues),
72+
instanceLocation: Instance.uri(instance),
73+
schemaLocations: constSchemaLocations.length ? constSchemaLocations : enumSchemaLocations
74+
}];
8875
}
89-
return errors;
9076
};
9177

92-
/**
93-
* @param {Json} val
94-
* @returns {string}
95-
*/
96-
const toKey = (val) => jsonStringify(val);
97-
9878
export default constAndEnumErrorHandler;

src/localization.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,18 @@ export class Localization {
5959
});
6060
}
6161

62-
/** @type (expected: Json) => string */
63-
getConstErrorMessage(expected) {
64-
return this.#formatMessage("const-message", {
65-
expected: JSON.stringify(expected, null, " ")
66-
});
67-
}
68-
6962
/** @type (expected: Json[]) => string */
7063
getEnumErrorMessage(expected) {
71-
const expectedJson = expected.map((value) => JSON.stringify(value));
72-
return this.#formatMessage("enum-message", {
73-
expected: this.disjunction.format(expectedJson)
74-
});
64+
if (expected.length === 1) {
65+
return this.#formatMessage("const-message", {
66+
expected: JSON.stringify(expected[0], null, " ")
67+
});
68+
} else {
69+
const expectedJson = expected.map((value) => JSON.stringify(value));
70+
return this.#formatMessage("enum-message", {
71+
expected: this.disjunction.format(expectedJson)
72+
});
73+
}
7574
}
7675

7776
/** @type (format: string) => string */

src/test-suite/tests/const.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
"messageId": "boolean-schema-message",
4646
"messageParams": {},
4747
"instanceLocation": "#",
48-
"schemaLocations": ["#/allOf/0/const", "#/allOf/1/const"]
48+
"schemaLocations": [
49+
"#/allOf/0/const",
50+
"#/allOf/1/const"
51+
]
4952
}
5053
]
5154
}

src/test-suite/tests/constAndEnum.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
"compatibility": "6",
2929
"schema": {
3030
"allOf": [
31-
{ "enum": [{"a": 1, "b": 2}, {"c": 3}] },
32-
{ "const": {"b": 2, "a": 1} }
31+
{ "enum": [{ "a": 1, "b": 2 }, { "c": 3 }] },
32+
{ "const": { "b": 2, "a": 1 } }
3333
]
3434
},
3535
"instance": "x",
@@ -63,7 +63,6 @@
6363
},
6464
"instanceLocation": "#",
6565
"schemaLocations": [
66-
"#/allOf/0/enum",
6766
"#/allOf/1/enum",
6867
"#/allOf/2/enum"
6968
]

src/test-suite/tests/enum.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@
3838
"count": 2
3939
},
4040
"instanceLocation": "#",
41-
"schemaLocations": ["#/allOf/1/enum"]
41+
"schemaLocations": [
42+
"#/allOf/0/enum",
43+
"#/allOf/1/enum"
44+
]
4245
}
4346
]
4447
},
@@ -65,7 +68,10 @@
6568
"messageId": "boolean-schema-message",
6669
"messageParams": {},
6770
"instanceLocation": "#",
68-
"schemaLocations": ["#/allOf/0/enum", "#/allOf/1/enum"]
71+
"schemaLocations": [
72+
"#/allOf/0/enum",
73+
"#/allOf/1/enum"
74+
]
6975
}
7076
]
7177
}

0 commit comments

Comments
 (0)