.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / table / node_modules / ajv / lib / vocabularies / discriminator / index.ts
1 import type {CodeKeywordDefinition, AnySchemaObject, KeywordErrorDefinition} from "../../types"
2 import type {KeywordCxt} from "../../compile/validate"
3 import {_, getProperty, Name} from "../../compile/codegen"
4 import {DiscrError, DiscrErrorObj} from "../discriminator/types"
5
6 export type DiscriminatorError = DiscrErrorObj<DiscrError.Tag> | DiscrErrorObj<DiscrError.Mapping>
7
8 const 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
17 const 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
102 export default def