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 = { 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 = { keyword : "contains" , type : "array" , schemaType : [ "object" , "boolean" ] , before : "uniqueItems" , trackErrors : true , error , code ( cxt ) { const { gen , schema , parentSchema , data , it } = cxt ; let min ; let max ; 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 ( ) { const schValid = gen . name ( "_valid" ) ; const count = gen . let ( "count" , 0 ) ; validateItems ( schValid , ( ) => gen . if ( schValid , ( ) => checkLimits ( count ) ) ) ; } function validateItems ( _valid , block ) { gen . forRange ( "i" , 0 , len , ( i ) => { cxt . subschema ( { keyword : "contains" , dataProp : i , dataPropType : Type . Num , compositeRule : true , } , _valid ) ; block ( ) ; } ) ; } function checkLimits ( count ) { 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 ;