123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import type {
- CodeKeywordDefinition,
- KeywordErrorDefinition,
- ErrorObject,
- AnySchema,
- } from "../../types"
- import type {KeywordCxt} from "../../compile/validate"
- import {_, str, Name} from "../../compile/codegen"
- import {alwaysValidSchema, checkStrictMode, Type} from "../../compile/util"
- export type ContainsError = ErrorObject<
- "contains",
- {minContains: number; maxContains?: number},
- AnySchema
- >
- const error: KeywordErrorDefinition = {
- message: ({params: {min, max}}) =>
- max === undefined
- ? str`must contain at least ${min} valid item(s)`
- : str`must contain at least ${min} and no more than ${max} valid item(s)`,
- params: ({params: {min, max}}) =>
- max === undefined ? _`{minContains: ${min}}` : _`{minContains: ${min}, maxContains: ${max}}`,
- }
- const def: CodeKeywordDefinition = {
- keyword: "contains",
- type: "array",
- schemaType: ["object", "boolean"],
- before: "uniqueItems",
- trackErrors: true,
- error,
- code(cxt: KeywordCxt) {
- const {gen, schema, parentSchema, data, it} = cxt
- let min: number
- let max: number | undefined
- const {minContains, maxContains} = parentSchema
- if (it.opts.next) {
- min = minContains === undefined ? 1 : minContains
- max = maxContains
- } else {
- min = 1
- }
- const len = gen.const("len", _`${data}.length`)
- cxt.setParams({min, max})
- if (max === undefined && min === 0) {
- checkStrictMode(it, `"minContains" == 0 without "maxContains": "contains" keyword ignored`)
- return
- }
- if (max !== undefined && min > max) {
- checkStrictMode(it, `"minContains" > "maxContains" is always invalid`)
- cxt.fail()
- return
- }
- if (alwaysValidSchema(it, schema)) {
- let cond = _`${len} >= ${min}`
- if (max !== undefined) cond = _`${cond} && ${len} <= ${max}`
- cxt.pass(cond)
- return
- }
- it.items = true
- const valid = gen.name("valid")
- if (max === undefined && min === 1) {
- validateItems(valid, () => gen.if(valid, () => gen.break()))
- } else if (min === 0) {
- gen.let(valid, true)
- if (max !== undefined) gen.if(_`${data}.length > 0`, validateItemsWithCount)
- } else {
- gen.let(valid, false)
- validateItemsWithCount()
- }
- cxt.result(valid, () => cxt.reset())
- function validateItemsWithCount(): void {
- const schValid = gen.name("_valid")
- const count = gen.let("count", 0)
- validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)))
- }
- function validateItems(_valid: Name, block: () => void): void {
- gen.forRange("i", 0, len, (i) => {
- cxt.subschema(
- {
- keyword: "contains",
- dataProp: i,
- dataPropType: Type.Num,
- compositeRule: true,
- },
- _valid
- )
- block()
- })
- }
- function checkLimits(count: Name): void {
- gen.code(_`${count}++`)
- if (max === undefined) {
- gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true).break())
- } else {
- gen.if(_`${count} > ${max}`, () => gen.assign(valid, false).break())
- if (min === 1) gen.assign(valid, true)
- else gen.if(_`${count} >= ${min}`, () => gen.assign(valid, true))
- }
- }
- },
- }
- export default def
|