json-schema-describes-subset
Version:
Tools for static JSON schema analysis, including functions to determine if one schema describes a subset of another or if a schema describes the empty set or to convert a schema to its disjunctive normal form (DNF).
100 lines • 3.53 kB
JavaScript
import { isLogicLiteral, AllOfSchema, AnyOfSchema, negateAtomicSchema, isAtomicSchema, NotSchema, } from '../atomic-schema.js';
export function negate(schema) {
if (isAtomicSchema(schema)) {
/* this shoulf be called only once */
return negateAtomicSchema(schema);
}
if (schema instanceof AllOfSchema) {
return new AnyOfSchema(schema.allOf.map(negate));
}
if (schema instanceof AnyOfSchema) {
return new AllOfSchema(schema.anyOf.map(negate));
}
// => schema instanceof NotSchema
return moveNotsToLiterals(schema.not);
}
/**
* Makes all the `not`s move down, so that `NOT`s are exclusively in the
* literals. `NOT`s are then resolved by applying {@link negateAtomicSchema}.
*
*/
export function moveNotsToLiterals(schema) {
if (schema instanceof AllOfSchema) {
return new AllOfSchema(schema.allOf.map(moveNotsToLiterals));
}
if (schema instanceof AnyOfSchema) {
return new AnyOfSchema(schema.anyOf.map(moveNotsToLiterals));
}
if (schema instanceof NotSchema) {
return negate(schema.not);
}
// schema is AtomicSchema
return schema;
}
/**
* Moves `AND`s down and `OR`s up to create the RawDNF.
*
*/
export function logicalCombinationOfLiteralsToRawDNFPart(schema) {
if (schema instanceof AnyOfSchema) {
return new AnyOfSchema(schema.anyOf
.map(logicalCombinationOfLiteralsToRawDNFPart)
.map((subschema) => {
if (subschema instanceof AnyOfSchema) {
/* anyOf as child of anyOf => flat */
return [...subschema.anyOf];
}
if (isLogicLiteral(subschema)) {
/* logicLiteral as child of anyOf => insert allOf */
return [new AllOfSchema([subschema])];
}
/* subschema is already a ConjunctionOfLiterals */
return [subschema];
})
.flat());
}
if (schema instanceof AllOfSchema) {
const children = schema.allOf.map(logicalCombinationOfLiteralsToRawDNFPart);
const dnfs = [];
const logicLiterals = [];
for (const child of children) {
if (child instanceof AllOfSchema) {
logicLiterals.push(...child.allOf);
}
else if (child instanceof AnyOfSchema) {
dnfs.push(child);
}
else {
logicLiterals.push(child);
}
}
if (dnfs.length === 0) {
return new AllOfSchema(logicLiterals);
}
const plainArrayDNF = dnfs.reduce((acc, { anyOf }) => {
const result = [];
for (const { allOf } of anyOf) {
for (const accItem of acc) {
result.push([...allOf, ...accItem]);
}
}
return result;
}, [logicLiterals]);
return new AnyOfSchema(plainArrayDNF.map((plainArrayConjunctionOfLiterals) => new AllOfSchema(plainArrayConjunctionOfLiterals)));
}
// schema is LogicLiteral
return schema;
}
export function dnfPartToRawDNF(dnfPart) {
if (isLogicLiteral(dnfPart)) {
return new AnyOfSchema([new AllOfSchema([dnfPart])]);
}
if (dnfPart instanceof AllOfSchema) {
return new AnyOfSchema([dnfPart]);
}
return dnfPart;
}
export function toRawDNF(schema) {
return dnfPartToRawDNF(logicalCombinationOfLiteralsToRawDNFPart(moveNotsToLiterals(schema)));
}
//# sourceMappingURL=to-raw-dnf.js.map