You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
4.3 KiB
192 lines
4.3 KiB
4 years ago
|
'use strict';
|
||
|
|
||
|
const Ref = require('./ref');
|
||
|
|
||
|
|
||
|
const internals = {};
|
||
|
|
||
|
|
||
|
internals.extendedCheckForValue = function (value, insensitive) {
|
||
|
|
||
|
const valueType = typeof value;
|
||
|
|
||
|
if (valueType === 'object') {
|
||
|
if (value instanceof Date) {
|
||
|
return (item) => {
|
||
|
|
||
|
return item instanceof Date && value.getTime() === item.getTime();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (Buffer.isBuffer(value)) {
|
||
|
return (item) => {
|
||
|
|
||
|
return Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary');
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
else if (insensitive && valueType === 'string') {
|
||
|
const lowercaseValue = value.toLowerCase();
|
||
|
return (item) => {
|
||
|
|
||
|
return typeof item === 'string' && lowercaseValue === item.toLowerCase();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
};
|
||
|
|
||
|
|
||
|
module.exports = class InternalSet {
|
||
|
|
||
|
constructor(from) {
|
||
|
|
||
|
this._set = new Set(from);
|
||
|
this._hasRef = false;
|
||
|
}
|
||
|
|
||
|
add(value, refs) {
|
||
|
|
||
|
const isRef = Ref.isRef(value);
|
||
|
if (!isRef && this.has(value, null, null, false)) {
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
if (refs !== undefined) { // If it's a merge, we don't have any refs
|
||
|
Ref.push(refs, value);
|
||
|
}
|
||
|
|
||
|
this._set.add(value);
|
||
|
|
||
|
this._hasRef |= isRef;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
merge(add, remove) {
|
||
|
|
||
|
for (const item of add._set) {
|
||
|
this.add(item);
|
||
|
}
|
||
|
|
||
|
for (const item of remove._set) {
|
||
|
this.remove(item);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
remove(value) {
|
||
|
|
||
|
this._set.delete(value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
has(value, state, options, insensitive) {
|
||
|
|
||
|
return !!this.get(value, state, options, insensitive);
|
||
|
}
|
||
|
|
||
|
get(value, state, options, insensitive) {
|
||
|
|
||
|
if (!this._set.size) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const hasValue = this._set.has(value);
|
||
|
if (hasValue) {
|
||
|
return { value };
|
||
|
}
|
||
|
|
||
|
const extendedCheck = internals.extendedCheckForValue(value, insensitive);
|
||
|
if (!extendedCheck) {
|
||
|
if (state && this._hasRef) {
|
||
|
for (let item of this._set) {
|
||
|
if (Ref.isRef(item)) {
|
||
|
item = [].concat(item(state.reference || state.parent, options));
|
||
|
const found = item.indexOf(value);
|
||
|
if (found >= 0) {
|
||
|
return { value: item[found] };
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return this._has(value, state, options, extendedCheck);
|
||
|
}
|
||
|
|
||
|
_has(value, state, options, check) {
|
||
|
|
||
|
const checkRef = !!(state && this._hasRef);
|
||
|
|
||
|
const isReallyEqual = function (item) {
|
||
|
|
||
|
if (value === item) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return check(item);
|
||
|
};
|
||
|
|
||
|
for (let item of this._set) {
|
||
|
if (checkRef && Ref.isRef(item)) { // Only resolve references if there is a state, otherwise it's a merge
|
||
|
item = item(state.reference || state.parent, options);
|
||
|
|
||
|
if (Array.isArray(item)) {
|
||
|
const found = item.findIndex(isReallyEqual);
|
||
|
if (found >= 0) {
|
||
|
return {
|
||
|
value: item[found]
|
||
|
};
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isReallyEqual(item)) {
|
||
|
return {
|
||
|
value: item
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
values(options) {
|
||
|
|
||
|
if (options && options.stripUndefined) {
|
||
|
const values = [];
|
||
|
|
||
|
for (const item of this._set) {
|
||
|
if (item !== undefined) {
|
||
|
values.push(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return values;
|
||
|
}
|
||
|
|
||
|
return Array.from(this._set);
|
||
|
}
|
||
|
|
||
|
slice() {
|
||
|
|
||
|
const set = new InternalSet(this._set);
|
||
|
set._hasRef = this._hasRef;
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
concat(source) {
|
||
|
|
||
|
const set = new InternalSet([...this._set, ...source._set]);
|
||
|
set._hasRef = !!(this._hasRef | source._hasRef);
|
||
|
return set;
|
||
|
}
|
||
|
};
|