import type {AnySchema} from "../../types" import type {SchemaObjCxt} from ".." import {_, str, getProperty, Code, Name} from "../codegen" import {escapeFragment, getErrorPath, Type} from "../util" import type {JSONType} from "../rules" export interface SubschemaContext { // TODO use Optional? align with SchemCxt property types schema: AnySchema schemaPath: Code errSchemaPath: string topSchemaRef?: Code errorPath?: Code dataLevel?: number dataTypes?: JSONType[] data?: Name parentData?: Name parentDataProperty?: Code | number dataNames?: Name[] dataPathArr?: (Code | number)[] propertyName?: Name jtdDiscriminator?: string jtdMetadata?: boolean compositeRule?: true createErrors?: boolean allErrors?: boolean } export type SubschemaArgs = Partial<{ keyword: string schemaProp: string | number schema: AnySchema schemaPath: Code errSchemaPath: string topSchemaRef: Code data: Name | Code dataProp: Code | string | number dataTypes: JSONType[] definedProperties: Set propertyName: Name dataPropType: Type jtdDiscriminator: string jtdMetadata: boolean compositeRule: true createErrors: boolean allErrors: boolean }> export function getSubschema( it: SchemaObjCxt, {keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef}: SubschemaArgs ): SubschemaContext { if (keyword !== undefined && schema !== undefined) { throw new Error('both "keyword" and "schema" passed, only one allowed') } if (keyword !== undefined) { const sch = it.schema[keyword] return schemaProp === undefined ? { schema: sch, schemaPath: _`${it.schemaPath}${getProperty(keyword)}`, errSchemaPath: `${it.errSchemaPath}/${keyword}`, } : { schema: sch[schemaProp], schemaPath: _`${it.schemaPath}${getProperty(keyword)}${getProperty(schemaProp)}`, errSchemaPath: `${it.errSchemaPath}/${keyword}/${escapeFragment(schemaProp)}`, } } if (schema !== undefined) { if (schemaPath === undefined || errSchemaPath === undefined || topSchemaRef === undefined) { throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"') } return { schema, schemaPath, topSchemaRef, errSchemaPath, } } throw new Error('either "keyword" or "schema" must be passed') } export function extendSubschemaData( subschema: SubschemaContext, it: SchemaObjCxt, {dataProp, dataPropType: dpType, data, dataTypes, propertyName}: SubschemaArgs ): void { if (data !== undefined && dataProp !== undefined) { throw new Error('both "data" and "dataProp" passed, only one allowed') } const {gen} = it if (dataProp !== undefined) { const {errorPath, dataPathArr, opts} = it const nextData = gen.let("data", _`${it.data}${getProperty(dataProp)}`, true) dataContextProps(nextData) subschema.errorPath = str`${errorPath}${getErrorPath(dataProp, dpType, opts.jsPropertySyntax)}` subschema.parentDataProperty = _`${dataProp}` subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty] } if (data !== undefined) { const nextData = data instanceof Name ? data : gen.let("data", data, true) // replaceable if used once? dataContextProps(nextData) if (propertyName !== undefined) subschema.propertyName = propertyName // TODO something is possibly wrong here with not changing parentDataProperty and not appending dataPathArr } if (dataTypes) subschema.dataTypes = dataTypes function dataContextProps(_nextData: Name): void { subschema.data = _nextData subschema.dataLevel = it.dataLevel + 1 subschema.dataTypes = [] it.definedProperties = new Set() subschema.parentData = it.data subschema.dataNames = [...it.dataNames, _nextData] } } export function extendSubschemaMode( subschema: SubschemaContext, {jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors}: SubschemaArgs ): void { if (compositeRule !== undefined) subschema.compositeRule = compositeRule if (createErrors !== undefined) subschema.createErrors = createErrors if (allErrors !== undefined) subschema.allErrors = allErrors subschema.jtdDiscriminator = jtdDiscriminator // not inherited subschema.jtdMetadata = jtdMetadata // not inherited }