dataclasses/predicates.js

  1. const { merge } = require('ramda');
  2. const $_type = require('./$_type');
  3. module.exports = ({ invariants }) => {
  4. /**
  5. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  6. * @typedef {object} Predicate
  7. * @memberof dataclasses
  8. */
  9. /**
  10. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  11. * @param {Function} type - Predicate subtype function constructor
  12. * @return {dataclasses.Predicate}
  13. * @memberof dataclasses
  14. */
  15. function Predicate(type) {
  16. return invariants
  17. .PredicateTypeMustBeValid(type.$name, Predicate.Types)
  18. .then(() =>
  19. merge($_type(type.$name), {
  20. /**
  21. * $canBeRemoved specify if the predicate can be removed or not from the Predicates tree
  22. * @type {Boolean}
  23. * @memberof Predicate
  24. */
  25. $canBeRemoved: true,
  26. })
  27. );
  28. }
  29. Predicate.Types = {
  30. ComparisonPredicate: 'ComparisonPredicate',
  31. CompoundPredicate: 'CompoundPredicate',
  32. };
  33. /**
  34. * Abstract Predicate type, a Predicate is the union type of CompoundPredicate | ComparisonPredicate
  35. * @typedef {object} ComparisonPredicate
  36. * @param {string} target - unique id for this target
  37. * @param {string} operator - label that will be displayed for this target
  38. * @param {string} arguments - the type_id name this target has
  39. * @memberof dataclasses
  40. */
  41. /**
  42. * A specialized predicate that you use to compare expressions.
  43. * @param {dataclasses.Target} target
  44. * @param {dataclasses.Operator} operator
  45. * @param {Array<*>} args
  46. * @return {Promise<dataclasses.ComparisonPredicate>} yield a ComparisonPredicate or a rejected promise
  47. * @memberof dataclasses
  48. */
  49. function ComparisonPredicate(target, operator, args) {
  50. return Predicate(ComparisonPredicate).then(predicate =>
  51. merge(predicate, {
  52. target: target,
  53. operator: operator,
  54. arguments: args,
  55. })
  56. );
  57. }
  58. // by pass var. mangling from minify
  59. ComparisonPredicate.$name = Predicate.Types.ComparisonPredicate;
  60. /**
  61. * Yield true if `predicate` is a ComparisonPredicate
  62. * @param {dataclasses.Predicate} predicate {@link dataclasses.Predicate}
  63. * @return {Boolean}
  64. * @memberof dataclasses
  65. */
  66. ComparisonPredicate.is = predicate => {
  67. return (
  68. predicate && predicate.$_type === Predicate.Types.ComparisonPredicate
  69. );
  70. };
  71. /**
  72. * A specialized predicate that evaluates logical combinations of other predicates.
  73. * @param {dataclasses.LogicalType} logic The predicate logic
  74. * @param {Array<dataclasses.Predicate>} predicates predicates
  75. * @return {Promise<dataclasses.CompoundPredicate>} yield a {@link dataclasses.CompoundPredicate} or a {@link errors.CompoundPredicateMustHaveAtLeastOneSubPredicate} rejected promise
  76. * @memberof dataclasses
  77. */
  78. function CompoundPredicate(logic, predicates) {
  79. return invariants
  80. .CompoundPredicateMustHaveAtLeastOneSubPredicate(
  81. predicates,
  82. CompoundPredicate
  83. )
  84. .then(() => Predicate(CompoundPredicate))
  85. .then(predicate =>
  86. merge(predicate, {
  87. logic: logic,
  88. predicates: predicates,
  89. })
  90. );
  91. }
  92. // by pass var. mangling from minify
  93. CompoundPredicate.$name = Predicate.Types.CompoundPredicate;
  94. /**
  95. * Reduce through the predicates tree
  96. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  97. * @param {function} f accumulation function, f(acc, predicate, parents)
  98. * @param {T} acc accumulator
  99. * @param {Array} [parents=[]] path to the node, array of parents
  100. * @return {T} yield the accumulator
  101. * @memberof dataclasses
  102. */
  103. CompoundPredicate.reduce = function(compoundPredicate, f, acc, parents = []) {
  104. acc = f(acc, compoundPredicate, parents);
  105. return compoundPredicate.predicates.reduce((_acc, predicate, i) => {
  106. const _parents = parents.concat([compoundPredicate, [predicate, i]]);
  107. return CompoundPredicate.is(predicate)
  108. ? CompoundPredicate.reduce(predicate, f, _acc, _parents)
  109. : f(_acc, predicate, _parents);
  110. }, acc);
  111. };
  112. /**
  113. * Walk through the predicates tree
  114. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  115. * @param {Function} f(predicate) iterator function
  116. * @memberof dataclasses
  117. */
  118. CompoundPredicate.forEach = (compoundPredicate, f) => {
  119. CompoundPredicate.reduce(
  120. compoundPredicate,
  121. (_, predicate, __) => {
  122. f(predicate);
  123. },
  124. null
  125. );
  126. };
  127. /**
  128. * Yield true if `predicate` is a CompoundPredicate
  129. * @param {dataclasses.Predicate} predicate
  130. * @return {Boolean}
  131. * @memberof dataclasses
  132. */
  133. CompoundPredicate.is = predicate =>
  134. predicate && predicate.$_type === Predicate.Types.CompoundPredicate;
  135. return {
  136. Predicate,
  137. ComparisonPredicate,
  138. CompoundPredicate,
  139. };
  140. };