Spaces:
Sleeping
Sleeping
; | |
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; | |
} | |
}; | |