Description
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.
 
 

191 lines
4.3 KiB

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