oneOf.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import type {
  2. CodeKeywordDefinition,
  3. ErrorObject,
  4. KeywordErrorDefinition,
  5. AnySchema,
  6. } from "../../types"
  7. import type {KeywordCxt} from "../../compile/validate"
  8. import {_, Name} from "../../compile/codegen"
  9. import {alwaysValidSchema} from "../../compile/util"
  10. import {SchemaCxt} from "../../compile"
  11. export type OneOfError = ErrorObject<
  12. "oneOf",
  13. {passingSchemas: [number, number] | null},
  14. AnySchema[]
  15. >
  16. const error: KeywordErrorDefinition = {
  17. message: "must match exactly one schema in oneOf",
  18. params: ({params}) => _`{passingSchemas: ${params.passing}}`,
  19. }
  20. const def: CodeKeywordDefinition = {
  21. keyword: "oneOf",
  22. schemaType: "array",
  23. trackErrors: true,
  24. error,
  25. code(cxt: KeywordCxt) {
  26. const {gen, schema, parentSchema, it} = cxt
  27. /* istanbul ignore if */
  28. if (!Array.isArray(schema)) throw new Error("ajv implementation error")
  29. if (it.opts.discriminator && parentSchema.discriminator) return
  30. const schArr: AnySchema[] = schema
  31. const valid = gen.let("valid", false)
  32. const passing = gen.let("passing", null)
  33. const schValid = gen.name("_valid")
  34. cxt.setParams({passing})
  35. // TODO possibly fail straight away (with warning or exception) if there are two empty always valid schemas
  36. gen.block(validateOneOf)
  37. cxt.result(
  38. valid,
  39. () => cxt.reset(),
  40. () => cxt.error(true)
  41. )
  42. function validateOneOf(): void {
  43. schArr.forEach((sch: AnySchema, i: number) => {
  44. let schCxt: SchemaCxt | undefined
  45. if (alwaysValidSchema(it, sch)) {
  46. gen.var(schValid, true)
  47. } else {
  48. schCxt = cxt.subschema(
  49. {
  50. keyword: "oneOf",
  51. schemaProp: i,
  52. compositeRule: true,
  53. },
  54. schValid
  55. )
  56. }
  57. if (i > 0) {
  58. gen
  59. .if(_`${schValid} && ${valid}`)
  60. .assign(valid, false)
  61. .assign(passing, _`[${passing}, ${i}]`)
  62. .else()
  63. }
  64. gen.if(schValid, () => {
  65. gen.assign(valid, true)
  66. gen.assign(passing, i)
  67. if (schCxt) cxt.mergeEvaluated(schCxt, Name)
  68. })
  69. })
  70. }
  71. },
  72. }
  73. export default def