| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 | 
							- 'use strict';
 
- const Hoek = require('@hapi/hoek');
 
- const Any = require('../any');
 
- const Cast = require('../../cast');
 
- const Ref = require('../../ref');
 
- const internals = {};
 
- internals.Alternatives = class extends Any {
 
-     constructor() {
 
-         super();
 
-         this._type = 'alternatives';
 
-         this._invalids.remove(null);
 
-         this._inner.matches = [];
 
-     }
 
-     _init(...args) {
 
-         return args.length ? this.try(...args) : this;
 
-     }
 
-     _base(value, state, options) {
 
-         const errors = [];
 
-         const il = this._inner.matches.length;
 
-         const baseType = this._baseType;
 
-         for (let i = 0; i < il; ++i) {
 
-             const item = this._inner.matches[i];
 
-             if (!item.schema) {
 
-                 const schema = item.peek || item.is;
 
-                 const input = item.is ? item.ref(state.reference || state.parent, options) : value;
 
-                 const failed = schema._validate(input, null, options, state.parent).errors;
 
-                 if (failed) {
 
-                     if (item.otherwise) {
 
-                         return item.otherwise._validate(value, state, options);
 
-                     }
 
-                 }
 
-                 else if (item.then) {
 
-                     return item.then._validate(value, state, options);
 
-                 }
 
-                 if (i === (il - 1) && baseType) {
 
-                     return baseType._validate(value, state, options);
 
-                 }
 
-                 continue;
 
-             }
 
-             const result = item.schema._validate(value, state, options);
 
-             if (!result.errors) {     // Found a valid match
 
-                 return result;
 
-             }
 
-             errors.push(...result.errors);
 
-         }
 
-         if (errors.length) {
 
-             return { errors: this.createError('alternatives.child', { reason: errors }, state, options) };
 
-         }
 
-         return { errors: this.createError('alternatives.base', null, state, options) };
 
-     }
 
-     try(...schemas) {
 
-         schemas = Hoek.flatten(schemas);
 
-         Hoek.assert(schemas.length, 'Cannot add other alternatives without at least one schema');
 
-         const obj = this.clone();
 
-         for (let i = 0; i < schemas.length; ++i) {
 
-             const cast = Cast.schema(this._currentJoi, schemas[i]);
 
-             if (cast._refs.length) {
 
-                 obj._refs.push(...cast._refs);
 
-             }
 
-             obj._inner.matches.push({ schema: cast });
 
-         }
 
-         return obj;
 
-     }
 
-     when(condition, options) {
 
-         let schemaCondition = false;
 
-         Hoek.assert(Ref.isRef(condition) || typeof condition === 'string' || (schemaCondition = condition instanceof Any), 'Invalid condition:', condition);
 
-         Hoek.assert(options, 'Missing options');
 
-         Hoek.assert(typeof options === 'object', 'Invalid options');
 
-         if (schemaCondition) {
 
-             Hoek.assert(!options.hasOwnProperty('is'), '"is" can not be used with a schema condition');
 
-         }
 
-         else {
 
-             Hoek.assert(options.hasOwnProperty('is'), 'Missing "is" directive');
 
-         }
 
-         Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
 
-         const obj = this.clone();
 
-         let is;
 
-         if (!schemaCondition) {
 
-             is = Cast.schema(this._currentJoi, options.is);
 
-             if (options.is === null || !(Ref.isRef(options.is) || options.is instanceof Any)) {
 
-                 // Only apply required if this wasn't already a schema or a ref, we'll suppose people know what they're doing
 
-                 is = is.required();
 
-             }
 
-         }
 
-         const item = {
 
-             ref: schemaCondition ? null : Cast.ref(condition),
 
-             peek: schemaCondition ? condition : null,
 
-             is,
 
-             then: options.then !== undefined ? Cast.schema(this._currentJoi, options.then) : undefined,
 
-             otherwise: options.otherwise !== undefined ? Cast.schema(this._currentJoi, options.otherwise) : undefined
 
-         };
 
-         if (obj._baseType) {
 
-             item.then = item.then && obj._baseType.concat(item.then);
 
-             item.otherwise = item.otherwise && obj._baseType.concat(item.otherwise);
 
-         }
 
-         if (!schemaCondition) {
 
-             Ref.push(obj._refs, item.ref);
 
-             obj._refs.push(...item.is._refs);
 
-         }
 
-         if (item.then && item.then._refs.length) {
 
-             obj._refs.push(...item.then._refs);
 
-         }
 
-         if (item.otherwise && item.otherwise._refs.length) {
 
-             obj._refs.push(...item.otherwise._refs);
 
-         }
 
-         obj._inner.matches.push(item);
 
-         return obj;
 
-     }
 
-     label(name) {
 
-         const obj = super.label(name);
 
-         obj._inner.matches = obj._inner.matches.map((match) => {
 
-             if (match.schema) {
 
-                 return { schema: match.schema.label(name) };
 
-             }
 
-             match = Object.assign({}, match);
 
-             if (match.then) {
 
-                 match.then = match.then.label(name);
 
-             }
 
-             if (match.otherwise) {
 
-                 match.otherwise = match.otherwise.label(name);
 
-             }
 
-             return match;
 
-         });
 
-         return obj;
 
-     }
 
-     describe() {
 
-         const description = super.describe();
 
-         const alternatives = [];
 
-         for (let i = 0; i < this._inner.matches.length; ++i) {
 
-             const item = this._inner.matches[i];
 
-             if (item.schema) {
 
-                 // try()
 
-                 alternatives.push(item.schema.describe());
 
-             }
 
-             else {
 
-                 // when()
 
-                 const when = item.is ? {
 
-                     ref: item.ref.toString(),
 
-                     is: item.is.describe()
 
-                 } : {
 
-                     peek: item.peek.describe()
 
-                 };
 
-                 if (item.then) {
 
-                     when.then = item.then.describe();
 
-                 }
 
-                 if (item.otherwise) {
 
-                     when.otherwise = item.otherwise.describe();
 
-                 }
 
-                 alternatives.push(when);
 
-             }
 
-         }
 
-         description.alternatives = alternatives;
 
-         return description;
 
-     }
 
- };
 
- module.exports = new internals.Alternatives();
 
 
  |