| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- 'use strict';
- /*!
- * ignore
- */
- const SchemaUnionOptions = require('../options/schemaUnionOptions');
- const SchemaType = require('../schemaType');
- const firstValueSymbol = Symbol('firstValue');
- /*!
- * ignore
- */
- class Union extends SchemaType {
- /**
- * Create a Union schema type.
- *
- * @param {String} key the path in the schema for this schema type
- * @param {Object} options SchemaType-specific options (must have 'of' as array)
- * @param {Object} schemaOptions additional options from the schema this schematype belongs to
- * @param {Schema} parentSchema the schema this schematype belongs to
- */
- constructor(key, options, schemaOptions, parentSchema) {
- super(key, options, 'Union', parentSchema);
- if (!Array.isArray(options?.of) || options.of.length === 0) {
- throw new Error('Union schema type requires an array of types');
- }
- this.schemaTypes = options.of.map(obj => parentSchema.interpretAsType(key, obj, schemaOptions));
- }
- cast(val, doc, init, prev, options) {
- let firstValue = firstValueSymbol;
- let lastError;
- // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then
- // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value.
- // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union.
- // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types,
- // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number.
- for (let i = 0; i < this.schemaTypes.length; ++i) {
- try {
- const casted = this.schemaTypes[i].cast(val, doc, init, prev, options);
- if (casted === val) {
- return casted;
- }
- if (firstValue === firstValueSymbol) {
- firstValue = casted;
- }
- } catch (error) {
- lastError = error;
- }
- }
- if (firstValue !== firstValueSymbol) {
- return firstValue;
- }
- throw lastError;
- }
- // Setters also need to be aware of casting - we need to apply the setters of the entry in the union we choose.
- applySetters(val, doc, init, prev, options) {
- let firstValue = firstValueSymbol;
- let lastError;
- // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then
- // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value.
- // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union.
- // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types,
- // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number.
- for (let i = 0; i < this.schemaTypes.length; ++i) {
- try {
- let castedVal = this.schemaTypes[i]._applySetters(val, doc, init, prev, options);
- if (castedVal == null) {
- castedVal = this.schemaTypes[i]._castNullish(castedVal);
- } else {
- castedVal = this.schemaTypes[i].cast(castedVal, doc, init, prev, options);
- }
- if (castedVal === val) {
- return castedVal;
- }
- if (firstValue === firstValueSymbol) {
- firstValue = castedVal;
- }
- } catch (error) {
- lastError = error;
- }
- }
- if (firstValue !== firstValueSymbol) {
- return firstValue;
- }
- throw lastError;
- }
- clone() {
- const schematype = super.clone();
- schematype.schemaTypes = this.schemaTypes.map(schemaType => schemaType.clone());
- return schematype;
- }
- }
- /**
- * This schema type's name, to defend against minifiers that mangle
- * function names.
- *
- * @api public
- */
- Union.schemaName = 'Union';
- Union.defaultOptions = {};
- Union.prototype.OptionsConstructor = SchemaUnionOptions;
- module.exports = Union;
|