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 |