overload.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. 'use strict';
  2. // overloadDefs
  3. // self, overloadDefs
  4. var overload = module.exports = function () {
  5. var self, selfSet = false, overloadDefs;
  6. if (arguments.length === 1) {
  7. overloadDefs = arguments[0];
  8. } else {
  9. selfSet = true;
  10. self = arguments[0];
  11. overloadDefs = arguments[1];
  12. }
  13. return function () {
  14. if (!selfSet) {
  15. self = this;
  16. }
  17. var args = Array.prototype.slice.call(arguments);
  18. var overloadMatchData = findOverload(overloadDefs, args);
  19. if (!overloadMatchData) {
  20. throw new Error(createErrorMessage('No match found.', overloadDefs));
  21. }
  22. var overloadFn = overloadMatchData.def[overloadMatchData.def.length - 1];
  23. return overloadFn.apply(self, overloadMatchData.args);
  24. };
  25. };
  26. var findOverload = overload.findOverload = function (overloadDefs, args) {
  27. for (var i = 0; i < overloadDefs.length; i++) {
  28. if (i === overloadDefs.length - 1 && typeof(overloadDefs[i]) === 'function') {
  29. return { args: args, def: [overloadDefs[i]] };
  30. }
  31. var newArgs;
  32. if (newArgs = isMatch(overloadDefs[i], args)) {
  33. return { args: newArgs, def: overloadDefs[i] };
  34. }
  35. }
  36. return null;
  37. };
  38. function isMatch(overloadDef, args) {
  39. var overloadDefIdx;
  40. var argIdx;
  41. var newArgs = [];
  42. for (overloadDefIdx = 0, argIdx = 0; overloadDefIdx < overloadDef.length - 1; overloadDefIdx++) {
  43. if (typeof(overloadDef[overloadDefIdx]) !== 'function') {
  44. throw new Error("Invalid overload definition. Array should only contain functions.");
  45. }
  46. //console.log('overloadDef/arg:', overloadDef[overloadDefIdx], args[argIdx]);
  47. var result = overloadDef[overloadDefIdx](args[argIdx]);
  48. //console.log('result:', result);
  49. if (result) {
  50. if (result.hasOwnProperty('defaultValue')) {
  51. newArgs.push(result.defaultValue);
  52. } else {
  53. if (overloadDef[overloadDefIdx].optional && args[argIdx] === null) {
  54. argIdx++;
  55. newArgs.push(overloadDef[overloadDefIdx].defaultValue);
  56. continue;
  57. }
  58. newArgs.push(args[argIdx]);
  59. argIdx++;
  60. }
  61. } else {
  62. if (overloadDef[overloadDefIdx].optional) {
  63. newArgs.push(overloadDef[overloadDefIdx].defaultValue);
  64. continue;
  65. }
  66. return false;
  67. }
  68. }
  69. //console.log('compares', overloadDefIdx, overloadDef.length - 1, argIdx, args.length, newArgs.length);
  70. if (overloadDefIdx === overloadDef.length - 1 && argIdx >= args.length) {
  71. return newArgs;
  72. }
  73. return false;
  74. }
  75. function createErrorMessage(message, overloadDefs) {
  76. message += '\n';
  77. message += ' Possible matches:\n';
  78. for (var i = 0; i < overloadDefs.length; i++) {
  79. var overloadDef = overloadDefs[i];
  80. if (typeof(overloadDef) === 'function') {
  81. message += ' [default]\n';
  82. } else {
  83. var matchers = overloadDef.slice(0, overloadDef.length - 1);
  84. matchers = matchers.map(function (m) {
  85. if (!m) {
  86. return '[invalid argument definition]';
  87. }
  88. return m.name || m;
  89. });
  90. if (matchers.length === 0) {
  91. message += ' ()\n';
  92. } else {
  93. message += ' (' + matchers.join(', ') + ')\n';
  94. }
  95. }
  96. }
  97. return message;
  98. }
  99. // --- func
  100. overload.func = function func(arg) {
  101. return typeof(arg) === 'function';
  102. };
  103. overload.funcOptional = function funcOptional(arg) {
  104. if (!arg) {
  105. return true;
  106. }
  107. return overload.func(arg);
  108. };
  109. overload.funcOptional.optional = true;
  110. overload.funcOptionalWithDefault = function (def) {
  111. var fn = function funcOptionalWithDefault(arg) {
  112. if (arg === undefined) {
  113. return false;
  114. }
  115. return overload.func(arg);
  116. };
  117. fn.optional = true;
  118. fn.defaultValue = def;
  119. return fn;
  120. };
  121. // --- callback
  122. overload.callbackOptional = function callbackOptional(arg) {
  123. if (!arg) {
  124. return { defaultValue: function defaultCallback() {} };
  125. }
  126. return overload.func(arg);
  127. };
  128. overload.callbackOptional.optional = true;
  129. // --- string
  130. overload.string = function string(arg) {
  131. return typeof(arg) === 'string';
  132. };
  133. overload.stringOptional = function stringOptional(arg) {
  134. if (!arg) {
  135. return true;
  136. }
  137. return overload.string(arg);
  138. };
  139. overload.stringOptional.optional = true;
  140. overload.stringOptionalWithDefault = function (def) {
  141. var fn = function stringOptionalWithDefault(arg) {
  142. if (arg === undefined) {
  143. return false;
  144. }
  145. return overload.string(arg);
  146. };
  147. fn.optional = true;
  148. fn.defaultValue = def;
  149. return fn;
  150. };
  151. // --- number
  152. overload.number = function number(arg) {
  153. return typeof(arg) === 'number';
  154. };
  155. overload.numberOptional = function numberOptional(arg) {
  156. if (!arg) {
  157. return true;
  158. }
  159. return overload.number(arg);
  160. };
  161. overload.numberOptional.optional = true;
  162. overload.numberOptionalWithDefault = function (def) {
  163. var fn = function numberOptionalWithDefault(arg) {
  164. if (arg === undefined) {
  165. return false;
  166. }
  167. return overload.number(arg);
  168. };
  169. fn.optional = true;
  170. fn.defaultValue = def;
  171. return fn;
  172. };
  173. // --- array
  174. overload.array = function array(arg) {
  175. return arg instanceof Array;
  176. };
  177. overload.arrayOptional = function arrayOptional(arg) {
  178. if (!arg) {
  179. return true;
  180. }
  181. return overload.array(arg);
  182. };
  183. overload.arrayOptional.optional = true;
  184. overload.arrayOptionalWithDefault = function (def) {
  185. var fn = function arrayOptionalWithDefault(arg) {
  186. if (arg === undefined) {
  187. return false;
  188. }
  189. return overload.array(arg);
  190. };
  191. fn.optional = true;
  192. fn.defaultValue = def;
  193. return fn;
  194. };
  195. // --- object
  196. overload.object = function object(arg) {
  197. return typeof(arg) === 'object';
  198. };
  199. overload.objectOptional = function objectOptional(arg) {
  200. if (!arg) {
  201. return true;
  202. }
  203. return overload.object(arg);
  204. };
  205. overload.objectOptional.optional = true;
  206. overload.objectOptionalWithDefault = function (def) {
  207. var fn = function objectOptionalWithDefault(arg) {
  208. if (arg === undefined) {
  209. return false;
  210. }
  211. return overload.object(arg);
  212. };
  213. fn.optional = true;
  214. fn.defaultValue = def;
  215. return fn;
  216. };