commands/validation.js

  1. /**
  2. * Command validation functions and utils.
  3. * @namespace validation
  4. * @memberof commands
  5. */
  6. //////////////////////////////////////////////////////////////////////////////
  7. // Validation function factories
  8. //////////////////////////////////////////////////////////////////////////////
  9. /**
  10. * A factory for a validation function based on a type.
  11. * @memberof commands.validation
  12. * @param {string} type - "Type" of the validation function. Any text that represents this type.
  13. * @param {string} [base=type] - Base JavaScript type: "string", "number", "boolean", etc.
  14. * @returns {commands.Validate} Validation function.
  15. */
  16. const makeType = (type, base = type) => {
  17. const v = e => typeof e === base; // eslint-disable-line valid-typeof
  18. v.exp = v.type = type;
  19. v.base = base;
  20. return v;
  21. };
  22. /**
  23. * A factory for a validation function based on a number.
  24. * @memberof commands.validation
  25. * @param {string} [type='number'] - "Type" of the validation function. Any text that represents this type.
  26. * @param {number} [min] - Minimum value.
  27. * @param {number} [max] - Maximum value.
  28. * @returns {commands.Validate} Validation function.
  29. */
  30. const makeNumber = (type = 'number', min = null, max = null) => {
  31. const hasMin = typeof min === 'number';
  32. const hasMax = typeof max === 'number';
  33. const v = n => {
  34. if (typeof n !== 'number') return false;
  35. if (hasMin && n < min) return false;
  36. if (hasMax && n > max) return false;
  37. return true;
  38. };
  39. v.base = 'number';
  40. v.exp = v.type = type;
  41. if (hasMin || hasMax) v.exp += ` [${hasMin?min:'-∞'},${hasMax?max:'+∞'}]`;
  42. if (hasMin) v.min = min;
  43. if (hasMax) v.max = max;
  44. return v;
  45. };
  46. /**
  47. * A factory for a validation function based on a number of string options.
  48. * @memberof commands.validation
  49. * @param {string} type - "Type" of the validation function. Any text that represents this type.
  50. * @param {string[]} options - Options the validation function accepts. Must not be empty.
  51. * @returns {commands.Validate} Validation function.
  52. */
  53. const makeOptions = (type, options) => {
  54. const v = e => options.indexOf(e) >= 0;
  55. v.base = 'string';
  56. v.exp = `"${options.join('" or "')}"`;
  57. v.type = type;
  58. v.options = options;
  59. return v;
  60. };
  61. /**
  62. * A factory for a validation function that combines other validation functions with "and".
  63. * @memberof commands.validation
  64. * @package
  65. * @param {...commands.Validate} vfs - Validation functions to combine.
  66. * @returns {commands.Validate} Combined validation function.
  67. */
  68. const and = (...vfs) => {
  69. vfs = [...new Set(vfs)]; // remove duplicates
  70. if (vfs.length === 1) return vfs[0];
  71. const v = e => vfs.every(vf => vf(e));
  72. v.base = 'object';
  73. v.exp = vfs.map(vf => vf.exp).join(' and ');
  74. return v;
  75. };
  76. //////////////////////////////////////////////////////////////////////////////
  77. // Validation functions
  78. //////////////////////////////////////////////////////////////////////////////
  79. /**
  80. * A validation function for an identifier.
  81. * @memberof commands.validation
  82. * @param {string} s - String to test.
  83. * @returns {boolean} If the input is a valid identifier.
  84. */
  85. const isIdentifier = s =>
  86. // A negative look behind to check for a string that does NOT end with a dash
  87. // is only supported on node 8.9.4 with the --harmony flag
  88. // https://node.green/#ES2018-features--RegExp-Lookbehind-Assertions
  89. // /^[a-z_][a-z_0-9-]*(?<!-)$/i
  90. /^[a-z_][a-z_0-9-]*$/i.test(s) && /[^-]$/.test(s);
  91. isIdentifier.exp = 'identifier';
  92. /**
  93. * A validation function for a string.
  94. * @memberof commands.validation
  95. * @param {object} e - Object to test.
  96. * @returns {boolean} If the input is of type 'string'.
  97. */
  98. const isString = makeType('string');
  99. /**
  100. * A validation function for a command.
  101. * @memberof commands.validation
  102. * @param {object} e - Object to test.
  103. * @returns {boolean} If the input is of type 'function', assumed to be a command function.
  104. */
  105. const isCommand = makeType('command', 'function');
  106. /**
  107. * A validation function for a number of milliseconds (mininum value 70).
  108. * @memberof commands.validation
  109. * @param {object} e - Object to test.
  110. * @returns {boolean} If the input is a number of milliseconds.
  111. */
  112. const isMs = makeNumber('ms', 70); // arbitrary lower limit for milliseconds
  113. /**
  114. * A validation function for a number of seconds (mininum value 1).
  115. * @memberof commands.validation
  116. * @param {object} e - Object to test.
  117. * @returns {boolean} If the input is a number of seconds.
  118. */
  119. const isSeconds = makeNumber('second', 1);
  120. /**
  121. * A validation function for a number of minutes (mininum value 1).
  122. * @memberof commands.validation
  123. * @param {object} e - Object to test.
  124. * @returns {boolean} If the input is a number of minutes.
  125. */
  126. const isMinutes = makeNumber('minute', 1);
  127. /**
  128. * A validation function for a number of times (mininum value 1).
  129. * @memberof commands.validation
  130. * @param {object} e - Object to test.
  131. * @returns {boolean} If the input is a number of times.
  132. */
  133. const isTimes = makeNumber('times', 1);
  134. //////////////////////////////////////////////////////////////////////////////
  135. module.exports = {
  136. makeType,
  137. makeNumber,
  138. makeOptions,
  139. and,
  140. isIdentifier,
  141. isString,
  142. isCommand,
  143. isMs,
  144. isSeconds,
  145. isMinutes,
  146. isTimes
  147. };