lib/vocabularies/jtd/discriminator.ts
import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types"
import type {KeywordCxt} from "../../compile/validate"
import {_, not, getProperty, Name} from "../../compile/codegen"
import {checkMetadata} from "./metadata"
import {checkNullableObject} from "./nullable"
import {typeErrorMessage, typeErrorParams, _JTDTypeError} from "./error"
import {DiscrError, DiscrErrorObj} from "../discriminator/types"
export type JTDDiscriminatorError =
| _JTDTypeError<"discriminator", "object", string>
| DiscrErrorObj<DiscrError.Tag>
| DiscrErrorObj<DiscrError.Mapping>
const error: KeywordErrorDefinition = {
message: (cxt) => {
const {schema, params} = cxt
return params.discrError
? params.discrError === DiscrError.Tag
? `tag "${schema}" must be string`
: `value of tag "${schema}" must be in mapping`
: typeErrorMessage(cxt, "object")
},
params: (cxt) => {
const {schema, params} = cxt
return params.discrError
? _`{error: ${params.discrError}, tag: ${schema}, tagValue: ${params.tag}}`
: typeErrorParams(cxt, "object")
},
}
const def: CodeKeywordDefinition = {
keyword: "discriminator",
schemaType: "string",
implements: ["mapping"],
error,
code(cxt: KeywordCxt) {
checkMetadata(cxt)
const {gen, data, schema, parentSchema} = cxt
const [valid, cond] = checkNullableObject(cxt, data)
gen.if(cond)
validateDiscriminator()
gen.elseIf(not(valid))
cxt.error()
gen.endIf()
cxt.ok(valid)
function validateDiscriminator(): void {
const tag = gen.const("tag", _`${data}${getProperty(schema)}`)
gen.if(_`${tag} === undefined`)
cxt.error(false, {discrError: DiscrError.Tag, tag})
gen.elseIf(_`typeof ${tag} == "string"`)
validateMapping(tag)
gen.else()
cxt.error(false, {discrError: DiscrError.Tag, tag}, {instancePath: schema})
gen.endIf()
}
function validateMapping(tag: Name): void {
gen.if(false)
for (const tagValue in parentSchema.mapping) {
gen.elseIf(_`${tag} === ${tagValue}`)
gen.assign(valid, applyTagSchema(tagValue))
}
gen.else()
cxt.error(
false,
{discrError: DiscrError.Mapping, tag},
{instancePath: schema, schemaPath: "mapping", parentSchema: true}
)
gen.endIf()
}
function applyTagSchema(schemaProp: string): Name {
const _valid = gen.name("valid")
cxt.subschema(
{
keyword: "mapping",
schemaProp,
jtdDiscriminator: schema,
},
_valid
)
return _valid
}
},
}
export default def