oneOf.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import type {
  2. CodeKeywordDefinition,
  3. ErrorObject,
  4. KeywordErrorDefinition,
  5. AnySchema,
  6. } from "../../types"
  7. import type KeywordCxt from "../../compile/context"
  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: "should 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, it} = cxt
  27. /* istanbul ignore if */
  28. if (!Array.isArray(schema)) throw new Error("ajv implementation error")
  29. const schArr: AnySchema[] = schema
  30. const valid = gen.let("valid", false)
  31. const passing = gen.let("passing", null)
  32. const schValid = gen.name("_valid")
  33. cxt.setParams({passing})
  34. // TODO possibly fail straight away (with warning or exception) if there are two empty always valid schemas
  35. gen.block(validateOneOf)
  36. cxt.result(
  37. valid,
  38. () => cxt.reset(),
  39. () => cxt.error(true)
  40. )
  41. function validateOneOf(): void {
  42. schArr.forEach((sch: AnySchema, i: number) => {
  43. let schCxt: SchemaCxt | undefined
  44. if (alwaysValidSchema(it, sch)) {
  45. gen.var(schValid, true)
  46. } else {
  47. schCxt = cxt.subschema(
  48. {
  49. keyword: "oneOf",
  50. schemaProp: i,
  51. compositeRule: true,
  52. },
  53. schValid
  54. )
  55. }
  56. if (i > 0) {
  57. gen
  58. .if(_`${schValid} && ${valid}`)
  59. .assign(valid, false)
  60. .assign(passing, _`[${passing}, ${i}]`)
  61. .else()
  62. }
  63. gen.if(schValid, () => {
  64. gen.assign(valid, true)
  65. gen.assign(passing, i)
  66. if (schCxt) cxt.mergeEvaluated(schCxt, Name)
  67. })
  68. })
  69. }
  70. },
  71. }
  72. export default def