source: trip-planner-front/node_modules/ajv/lib/vocabularies/discriminator/index.ts@ 84d0fbb

Last change on this file since 84d0fbb was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 3.7 KB
Line 
1import type {CodeKeywordDefinition, AnySchemaObject, KeywordErrorDefinition} from "../../types"
2import type {KeywordCxt} from "../../compile/validate"
3import {_, getProperty, Name} from "../../compile/codegen"
4import {DiscrError, DiscrErrorObj} from "../discriminator/types"
5
6export type DiscriminatorError = DiscrErrorObj<DiscrError.Tag> | DiscrErrorObj<DiscrError.Mapping>
7
8const error: KeywordErrorDefinition = {
9 message: ({params: {discrError, tagName}}) =>
10 discrError === DiscrError.Tag
11 ? `tag "${tagName}" must be string`
12 : `value of tag "${tagName}" must be in oneOf`,
13 params: ({params: {discrError, tag, tagName}}) =>
14 _`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`,
15}
16
17const def: CodeKeywordDefinition = {
18 keyword: "discriminator",
19 type: "object",
20 schemaType: "object",
21 error,
22 code(cxt: KeywordCxt) {
23 const {gen, data, schema, parentSchema, it} = cxt
24 const {oneOf} = parentSchema
25 if (!it.opts.discriminator) {
26 throw new Error("discriminator: requires discriminator option")
27 }
28 const tagName = schema.propertyName
29 if (typeof tagName != "string") throw new Error("discriminator: requires propertyName")
30 if (schema.mapping) throw new Error("discriminator: mapping is not supported")
31 if (!oneOf) throw new Error("discriminator: requires oneOf keyword")
32 const valid = gen.let("valid", false)
33 const tag = gen.const("tag", _`${data}${getProperty(tagName)}`)
34 gen.if(
35 _`typeof ${tag} == "string"`,
36 () => validateMapping(),
37 () => cxt.error(false, {discrError: DiscrError.Tag, tag, tagName})
38 )
39 cxt.ok(valid)
40
41 function validateMapping(): void {
42 const mapping = getMapping()
43 gen.if(false)
44 for (const tagValue in mapping) {
45 gen.elseIf(_`${tag} === ${tagValue}`)
46 gen.assign(valid, applyTagSchema(mapping[tagValue]))
47 }
48 gen.else()
49 cxt.error(false, {discrError: DiscrError.Mapping, tag, tagName})
50 gen.endIf()
51 }
52
53 function applyTagSchema(schemaProp?: number): Name {
54 const _valid = gen.name("valid")
55 const schCxt = cxt.subschema({keyword: "oneOf", schemaProp}, _valid)
56 cxt.mergeEvaluated(schCxt, Name)
57 return _valid
58 }
59
60 function getMapping(): {[T in string]?: number} {
61 const oneOfMapping: {[T in string]?: number} = {}
62 const topRequired = hasRequired(parentSchema)
63 let tagRequired = true
64 for (let i = 0; i < oneOf.length; i++) {
65 const sch = oneOf[i]
66 const propSch = sch.properties?.[tagName]
67 if (typeof propSch != "object") {
68 throw new Error(`discriminator: oneOf schemas must have "properties/${tagName}"`)
69 }
70 tagRequired = tagRequired && (topRequired || hasRequired(sch))
71 addMappings(propSch, i)
72 }
73 if (!tagRequired) throw new Error(`discriminator: "${tagName}" must be required`)
74 return oneOfMapping
75
76 function hasRequired({required}: AnySchemaObject): boolean {
77 return Array.isArray(required) && required.includes(tagName)
78 }
79
80 function addMappings(sch: AnySchemaObject, i: number): void {
81 if (sch.const) {
82 addMapping(sch.const, i)
83 } else if (sch.enum) {
84 for (const tagValue of sch.enum) {
85 addMapping(tagValue, i)
86 }
87 } else {
88 throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`)
89 }
90 }
91
92 function addMapping(tagValue: unknown, i: number): void {
93 if (typeof tagValue != "string" || tagValue in oneOfMapping) {
94 throw new Error(`discriminator: "${tagName}" values must be unique strings`)
95 }
96 oneOfMapping[tagValue] = i
97 }
98 }
99 },
100}
101
102export default def
Note: See TracBrowser for help on using the repository browser.