enum.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. import type {CodeKeywordDefinition, KeywordErrorDefinition, ErrorObject} from "../../types"
  2. import type {KeywordCxt} from "../../compile/validate"
  3. import {_, or, and, Code} from "../../compile/codegen"
  4. import {checkMetadata} from "./metadata"
  5. import {checkNullable} from "./nullable"
  6. export type JTDEnumError = ErrorObject<"enum", {allowedValues: string[]}, string[]>
  7. const error: KeywordErrorDefinition = {
  8. message: "must be equal to one of the allowed values",
  9. params: ({schemaCode}) => _`{allowedValues: ${schemaCode}}`,
  10. }
  11. const def: CodeKeywordDefinition = {
  12. keyword: "enum",
  13. schemaType: "array",
  14. error,
  15. code(cxt: KeywordCxt) {
  16. checkMetadata(cxt)
  17. const {gen, data, schema, schemaValue, parentSchema, it} = cxt
  18. if (schema.length === 0) throw new Error("enum must have non-empty array")
  19. if (schema.length !== new Set(schema).size) throw new Error("enum items must be unique")
  20. let valid: Code
  21. const isString = _`typeof ${data} == "string"`
  22. if (schema.length >= it.opts.loopEnum) {
  23. let cond: Code
  24. ;[valid, cond] = checkNullable(cxt, isString)
  25. gen.if(cond, loopEnum)
  26. } else {
  27. /* istanbul ignore if */
  28. if (!Array.isArray(schema)) throw new Error("ajv implementation error")
  29. valid = and(isString, or(...schema.map((value: string) => _`${data} === ${value}`)))
  30. if (parentSchema.nullable) valid = or(_`${data} === null`, valid)
  31. }
  32. cxt.pass(valid)
  33. function loopEnum(): void {
  34. gen.forOf("v", schemaValue as Code, (v) =>
  35. gen.if(_`${valid} = ${data} === ${v}`, () => gen.break())
  36. )
  37. }
  38. },
  39. }
  40. export default def