set.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. 'use strict';
  2. const Ref = require('./ref');
  3. const internals = {};
  4. internals.extendedCheckForValue = function (value, insensitive) {
  5. const valueType = typeof value;
  6. if (valueType === 'object') {
  7. if (value instanceof Date) {
  8. return (item) => {
  9. return item instanceof Date && value.getTime() === item.getTime();
  10. };
  11. }
  12. if (Buffer.isBuffer(value)) {
  13. return (item) => {
  14. return Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary');
  15. };
  16. }
  17. }
  18. else if (insensitive && valueType === 'string') {
  19. const lowercaseValue = value.toLowerCase();
  20. return (item) => {
  21. return typeof item === 'string' && lowercaseValue === item.toLowerCase();
  22. };
  23. }
  24. return null;
  25. };
  26. module.exports = class InternalSet {
  27. constructor(from) {
  28. this._set = new Set(from);
  29. this._hasRef = false;
  30. }
  31. add(value, refs) {
  32. const isRef = Ref.isRef(value);
  33. if (!isRef && this.has(value, null, null, false)) {
  34. return this;
  35. }
  36. if (refs !== undefined) { // If it's a merge, we don't have any refs
  37. Ref.push(refs, value);
  38. }
  39. this._set.add(value);
  40. this._hasRef |= isRef;
  41. return this;
  42. }
  43. merge(add, remove) {
  44. for (const item of add._set) {
  45. this.add(item);
  46. }
  47. for (const item of remove._set) {
  48. this.remove(item);
  49. }
  50. return this;
  51. }
  52. remove(value) {
  53. this._set.delete(value);
  54. return this;
  55. }
  56. has(value, state, options, insensitive) {
  57. return !!this.get(value, state, options, insensitive);
  58. }
  59. get(value, state, options, insensitive) {
  60. if (!this._set.size) {
  61. return false;
  62. }
  63. const hasValue = this._set.has(value);
  64. if (hasValue) {
  65. return { value };
  66. }
  67. const extendedCheck = internals.extendedCheckForValue(value, insensitive);
  68. if (!extendedCheck) {
  69. if (state && this._hasRef) {
  70. for (let item of this._set) {
  71. if (Ref.isRef(item)) {
  72. item = [].concat(item(state.reference || state.parent, options));
  73. const found = item.indexOf(value);
  74. if (found >= 0) {
  75. return { value: item[found] };
  76. }
  77. }
  78. }
  79. }
  80. return false;
  81. }
  82. return this._has(value, state, options, extendedCheck);
  83. }
  84. _has(value, state, options, check) {
  85. const checkRef = !!(state && this._hasRef);
  86. const isReallyEqual = function (item) {
  87. if (value === item) {
  88. return true;
  89. }
  90. return check(item);
  91. };
  92. for (let item of this._set) {
  93. if (checkRef && Ref.isRef(item)) { // Only resolve references if there is a state, otherwise it's a merge
  94. item = item(state.reference || state.parent, options);
  95. if (Array.isArray(item)) {
  96. const found = item.findIndex(isReallyEqual);
  97. if (found >= 0) {
  98. return {
  99. value: item[found]
  100. };
  101. }
  102. continue;
  103. }
  104. }
  105. if (isReallyEqual(item)) {
  106. return {
  107. value: item
  108. };
  109. }
  110. }
  111. return false;
  112. }
  113. values(options) {
  114. if (options && options.stripUndefined) {
  115. const values = [];
  116. for (const item of this._set) {
  117. if (item !== undefined) {
  118. values.push(item);
  119. }
  120. }
  121. return values;
  122. }
  123. return Array.from(this._set);
  124. }
  125. slice() {
  126. const set = new InternalSet(this._set);
  127. set._hasRef = this._hasRef;
  128. return set;
  129. }
  130. concat(source) {
  131. const set = new InternalSet([...this._set, ...source._set]);
  132. set._hasRef = !!(this._hasRef | source._hasRef);
  133. return set;
  134. }
  135. };