123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- 'use strict';
- const Assert = require('@hapi/hoek/lib/assert');
- const DeepEqual = require('@hapi/hoek/lib/deepEqual');
- const Common = require('./common');
- const internals = {};
- module.exports = internals.Values = class {
- constructor(values, refs) {
- this._values = new Set(values);
- this._refs = new Set(refs);
- this._lowercase = internals.lowercases(values);
- this._override = false;
- }
- get length() {
- return this._values.size + this._refs.size;
- }
- add(value, refs) {
- // Reference
- if (Common.isResolvable(value)) {
- if (!this._refs.has(value)) {
- this._refs.add(value);
- if (refs) { // Skipped in a merge
- refs.register(value);
- }
- }
- return;
- }
- // Value
- if (!this.has(value, null, null, false)) {
- this._values.add(value);
- if (typeof value === 'string') {
- this._lowercase.set(value.toLowerCase(), value);
- }
- }
- }
- static merge(target, source, remove) {
- target = target || new internals.Values();
- if (source) {
- if (source._override) {
- return source.clone();
- }
- for (const item of [...source._values, ...source._refs]) {
- target.add(item);
- }
- }
- if (remove) {
- for (const item of [...remove._values, ...remove._refs]) {
- target.remove(item);
- }
- }
- return target.length ? target : null;
- }
- remove(value) {
- // Reference
- if (Common.isResolvable(value)) {
- this._refs.delete(value);
- return;
- }
- // Value
- this._values.delete(value);
- if (typeof value === 'string') {
- this._lowercase.delete(value.toLowerCase());
- }
- }
- has(value, state, prefs, insensitive) {
- return !!this.get(value, state, prefs, insensitive);
- }
- get(value, state, prefs, insensitive) {
- if (!this.length) {
- return false;
- }
- // Simple match
- if (this._values.has(value)) {
- return { value };
- }
- // Case insensitive string match
- if (typeof value === 'string' &&
- value &&
- insensitive) {
- const found = this._lowercase.get(value.toLowerCase());
- if (found) {
- return { value: found };
- }
- }
- if (!this._refs.size &&
- typeof value !== 'object') {
- return false;
- }
- // Objects
- if (typeof value === 'object') {
- for (const item of this._values) {
- if (DeepEqual(item, value)) {
- return { value: item };
- }
- }
- }
- // References
- if (state) {
- for (const ref of this._refs) {
- const resolved = ref.resolve(value, state, prefs, null, { in: true });
- if (resolved === undefined) {
- continue;
- }
- const items = !ref.in || typeof resolved !== 'object'
- ? [resolved]
- : Array.isArray(resolved) ? resolved : Object.keys(resolved);
- for (const item of items) {
- if (typeof item !== typeof value) {
- continue;
- }
- if (insensitive &&
- value &&
- typeof value === 'string') {
- if (item.toLowerCase() === value.toLowerCase()) {
- return { value: item, ref };
- }
- }
- else {
- if (DeepEqual(item, value)) {
- return { value: item, ref };
- }
- }
- }
- }
- }
- return false;
- }
- override() {
- this._override = true;
- }
- values(options) {
- if (options &&
- options.display) {
- const values = [];
- for (const item of [...this._values, ...this._refs]) {
- if (item !== undefined) {
- values.push(item);
- }
- }
- return values;
- }
- return Array.from([...this._values, ...this._refs]);
- }
- clone() {
- const set = new internals.Values(this._values, this._refs);
- set._override = this._override;
- return set;
- }
- concat(source) {
- Assert(!source._override, 'Cannot concat override set of values');
- const set = new internals.Values([...this._values, ...source._values], [...this._refs, ...source._refs]);
- set._override = this._override;
- return set;
- }
- describe() {
- const normalized = [];
- if (this._override) {
- normalized.push({ override: true });
- }
- for (const value of this._values.values()) {
- normalized.push(value && typeof value === 'object' ? { value } : value);
- }
- for (const value of this._refs.values()) {
- normalized.push(value.describe());
- }
- return normalized;
- }
- };
- internals.Values.prototype[Common.symbols.values] = true;
- // Aliases
- internals.Values.prototype.slice = internals.Values.prototype.clone;
- // Helpers
- internals.lowercases = function (from) {
- const map = new Map();
- if (from) {
- for (const value of from) {
- if (typeof value === 'string') {
- map.set(value.toLowerCase(), value);
- }
- }
- }
- return map;
- };
|