|
- '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;
- };
|