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. /**
  59. * Yield true if `predicate` is a ComparisonPredicate
  60. * @param {dataclasses.Predicate} predicate {@link dataclasses.Predicate}
  61. * @return {Boolean}
  62. * @memberof dataclasses
  63. */
  64. ComparisonPredicate.is = predicate => {
  65. return (
  66. predicate && predicate.$_type === Predicate.Types.ComparisonPredicate
  67. );
  68. };
  69. /**
  70. * A specialized predicate that evaluates logical combinations of other predicates.
  71. * @param {dataclasses.LogicalType} logic The predicate logic
  72. * @param {Array<dataclasses.Predicate>} predicates predicates
  73. * @return {Promise<dataclasses.CompoundPredicate>} yield a {@link dataclasses.CompoundPredicate} or a {@link errors.CompoundPredicateMustHaveAtLeastOneSubPredicate} rejected promise
  74. * @memberof dataclasses
  75. */
  76. function CompoundPredicate(logic, predicates) {
  77. return invariants
  78. .CompoundPredicateMustHaveAtLeastOneSubPredicate(
  79. predicates,
  80. CompoundPredicate
  81. )
  82. .then(() => Predicate(CompoundPredicate))
  83. .then(predicate =>
  84. merge(predicate, {
  85. logic: logic,
  86. predicates: predicates,
  87. })
  88. );
  89. }
  90. /**
  91. * Reduce through the predicates tree
  92. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  93. * @param {function} f accumulation function, f(acc, predicate, parents)
  94. * @param {T} acc accumulator
  95. * @param {Array} [parents=[]] path to the node, array of parents
  96. * @return {T} yield the accumulator
  97. * @memberof dataclasses
  98. */
  99. CompoundPredicate.reduce = function(compoundPredicate, f, acc, parents = []) {
  100. acc = f(acc, compoundPredicate, parents);
  101. return compoundPredicate.predicates.reduce((_acc, predicate, i) => {
  102. const _parents = parents.concat([compoundPredicate, [predicate, i]]);
  103. return CompoundPredicate.is(predicate)
  104. ? CompoundPredicate.reduce(predicate, f, _acc, _parents)
  105. : f(_acc, predicate, _parents);
  106. }, acc);
  107. };
  108. /**
  109. * Walk through the predicates tree
  110. * @param {dataclasses.CompoundPredicate} compoundPredicate starter node
  111. * @param {Function} f(predicate) iterator function
  112. * @memberof dataclasses
  113. */
  114. CompoundPredicate.forEach = (compoundPredicate, f) => {
  115. CompoundPredicate.reduce(
  116. compoundPredicate,
  117. (_, predicate, __) => {
  118. f(predicate);
  119. },
  120. null
  121. );
  122. };
  123. /**
  124. * Yield true if `predicate` is a CompoundPredicate
  125. * @param {dataclasses.Predicate} predicate
  126. * @return {Boolean}
  127. * @memberof dataclasses
  128. */
  129. CompoundPredicate.is = predicate =>
  130. predicate && predicate.$_type === Predicate.Types.CompoundPredicate;
  131. return {
  132. Predicate,
  133. ComparisonPredicate,
  134. CompoundPredicate,
  135. };
  136. };