import { Schema, SchemaContextCreator } from '../schema'; type SchemaType> = T extends Schema ? U : never; type ArraySchemaType>> = T[number] extends Schema ? SchemaType : never; type DiscriminatorMap>> = { [K in ArraySchemaType]?: Schema>; }; export function anyOf>>( schemas: [...T], discriminatorMap?: DiscriminatorMap, discriminatorField?: string ): Schema> { if (discriminatorMap && discriminatorField) { return createAnyOfWithDiscriminator( schemas, discriminatorMap, discriminatorField ); } else { return createAnyOfWithoutDiscriminator(schemas); } } function createAnyOfWithDiscriminator>>( schemas: T, discriminatorMap: DiscriminatorMap, discriminatorField: string ): Schema> { return { type: () => `OneOf<${schemas.map((schema) => schema.type()).join(' | ')}>`, validateBeforeMap: (value, ctxt) => { const discriminatorValue = value && typeof value === 'object' && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].validateBeforeMap( value, ctxt ); } return matchAndValidateBeforeMap(schemas, value, ctxt); }, validateBeforeUnmap: (value, ctxt) => { const discriminatorValue = value && typeof value === 'object' && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].validateBeforeUnmap( value, ctxt ); } return matchAndValidateBeforeUnmap(schemas, value, ctxt); }, map: (value, ctxt) => { const discriminatorValue = value && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].map(value, ctxt); } return matchAndMap(schemas, value, ctxt); }, unmap: (value, ctxt) => { const discriminatorValue = value && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].unmap(value, ctxt); } return matchAndUnmap(schemas, value, ctxt); }, validateBeforeMapXml: (value, ctxt) => { const discriminatorValue = value && typeof value === 'object' && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].validateBeforeMapXml( value, ctxt ); } return matchAndValidateBeforeMapXml(schemas, value, ctxt); }, mapXml: (value, ctxt) => { const discriminatorValue = value && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].mapXml(value, ctxt); } return matchAndMapXml(schemas, value, ctxt); }, unmapXml: (value, ctxt) => { const discriminatorValue = value && value[discriminatorField]; if (discriminatorValue && discriminatorMap[discriminatorValue]) { return discriminatorMap[discriminatorValue].unmapXml(value, ctxt); } return matchAndUnmapXml(schemas, value, ctxt); }, }; } function createAnyOfWithoutDiscriminator>>( schemas: T ): Schema> { return { type: () => `OneOf<${schemas.map((schema) => schema.type()).join(' | ')}>`, validateBeforeMap: (value, ctxt) => matchAndValidateBeforeMap(schemas, value, ctxt), validateBeforeUnmap: (value, ctxt) => matchAndValidateBeforeUnmap(schemas, value, ctxt), map: (value, ctxt) => matchAndMap(schemas, value, ctxt), unmap: (value, ctxt) => matchAndUnmap(schemas, value, ctxt), validateBeforeMapXml: (value, ctxt) => matchAndValidateBeforeMapXml(schemas, value, ctxt), mapXml: (value, ctxt) => matchAndMapXml(schemas, value, ctxt), unmapXml: (value, ctxt) => matchAndUnmapXml(schemas, value, ctxt), }; } function matchAndValidateBeforeMap>>( schemas: T, value: unknown, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { const validationErrors = schema.validateBeforeMap(value, ctxt); if (validationErrors.length === 0) { matchedSchemas.push(schema); } } return validateSchemas(matchedSchemas, ctxt); } function matchAndValidateBeforeUnmap>>( schemas: T, value: unknown, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { const validationErrors = schema.validateBeforeUnmap(value, ctxt); if (validationErrors.length === 0) { matchedSchemas.push(schema); } } return validateSchemas(matchedSchemas, ctxt); } function matchAndValidateBeforeMapXml>>( schemas: T, value: unknown, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { const validationErrors = schema.validateBeforeMapXml(value, ctxt); if (validationErrors.length === 0) { matchedSchemas.push(schema); } } return validateSchemas(matchedSchemas, ctxt); } function validateSchemas>>( schemas: T, ctxt: SchemaContextCreator ) { if (schemas.length > 0) { return []; } return ctxt.fail('Could not match against any acceptable type.'); } function matchAndMap>>( schemas: T, value: any, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { if (schema.validateBeforeMap(value, ctxt).length === 0) { matchedSchemas.push(schema); } } return matchedSchemas.length > 0 ? matchedSchemas[0].map(value, ctxt) : undefined; } function matchAndUnmap>>( schemas: T, value: any, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { if (schema.validateBeforeUnmap(value, ctxt).length === 0) { matchedSchemas.push(schema); } } return matchedSchemas.length > 0 ? matchedSchemas[0].unmap(value, ctxt) : undefined; } function matchAndMapXml>>( schemas: T, value: any, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { if (schema.validateBeforeMapXml(value, ctxt).length === 0) { matchedSchemas.push(schema); } } return matchedSchemas.length > 0 ? matchedSchemas[0].mapXml(value, ctxt) : undefined; } function matchAndUnmapXml>>( schemas: T, value: any, ctxt: SchemaContextCreator ) { const matchedSchemas: Array> = []; for (const schema of schemas) { if (schema.validateBeforeMapXml(value, ctxt).length === 0) { matchedSchemas.push(schema); } } return matchedSchemas.length > 0 ? matchedSchemas[0].unmapXml(value, ctxt) : undefined; }