You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					110 lines
				
				3.2 KiB
			
		
		
			
		
	
	
					110 lines
				
				3.2 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								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
							 |