enum.ts 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
  2. import type KeywordCxt from "../../compile/context"
  3. import {_, or, Name, Code} from "../../compile/codegen"
  4. import equal = require("fast-deep-equal")
  5. export type EnumError = ErrorObject<"enum", {allowedValues: any[]}, any[] | {$data: string}>
  6. const error: KeywordErrorDefinition = {
  7. message: "should be equal to one of the allowed values",
  8. params: ({schemaCode}) => _`{allowedValues: ${schemaCode}}`,
  9. }
  10. const def: CodeKeywordDefinition = {
  11. keyword: "enum",
  12. schemaType: "array",
  13. $data: true,
  14. error,
  15. code(cxt: KeywordCxt) {
  16. const {gen, data, $data, schema, schemaCode, it} = cxt
  17. if (!$data && schema.length === 0) throw new Error("enum must have non-empty array")
  18. const useLoop = schema.length >= it.opts.loopEnum
  19. const eql = cxt.gen.scopeValue("func", {
  20. ref: equal,
  21. code: _`require("ajv/dist/compile/equal")`,
  22. })
  23. let valid: Code
  24. if (useLoop || $data) {
  25. valid = gen.let("valid")
  26. cxt.block$data(valid, loopEnum)
  27. } else {
  28. /* istanbul ignore if */
  29. if (!Array.isArray(schema)) throw new Error("ajv implementation error")
  30. const vSchema = gen.const("vSchema", schemaCode)
  31. valid = or(...schema.map((_x: unknown, i: number) => equalCode(vSchema, i)))
  32. }
  33. cxt.pass(valid)
  34. function loopEnum(): void {
  35. gen.assign(valid, false)
  36. gen.forOf("v", schemaCode as Code, (v) =>
  37. gen.if(_`${eql}(${data}, ${v})`, () => gen.assign(valid, true).break())
  38. )
  39. }
  40. function equalCode(vSchema: Name, i: number): Code {
  41. const sch = schema[i]
  42. return sch && typeof sch === "object"
  43. ? _`${eql}(${data}, ${vSchema}[${i}])`
  44. : _`${data} === ${sch}`
  45. }
  46. },
  47. }
  48. export default def