parse-human.js 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /**
  2. * Module dependencies
  3. */
  4. var util = require('util');
  5. var _ = require('@sailshq/lodash');
  6. var types = require('./helpers/types');
  7. var validate = require('./validate');
  8. var parse = require('./parse');
  9. var hydrate = require('./hydrate');
  10. /**
  11. * parseHuman()
  12. *
  13. * Convert a string that was entered by a human into a value of the appropriate type.
  14. * By default, parse numbers and booleans-- but otherwise leave it as a string.
  15. *
  16. * On the other hand, if the optional `expectedTypeSchema` is provided, use it to make a better guess:
  17. * • If the type schema is expecting a string, number, or boolean, then loose validation (`rttc.validate()`)
  18. * will be performed and the potentially-coerced result will be returned.
  19. * • If the type schema is expecting a function, then if `unsafeMode` is enabled, the human string will be hydrated
  20. * as a JavaScript function. Otherwise, if `unsafeMode` is false, an error will be thrown.
  21. * • Finally, if the type schema is expecting a dictionary, array, JSON, or any ref, then interpret the human string
  22. * as JSON (using rttc.parse(), with respect for `unsafeMode` re hydrating nested functions). If the string cannot
  23. * be parsed as JSON, an error is thrown. But if the rttc.parse() is successful, the result is then validated against
  24. * the type schema (using RTTC loose validation). If _that_ doesn't throw an error, then the result is returned.
  25. *
  26. * @param {String} humanString
  27. * @param {*} expectedTypeSchema [optional]
  28. * @param {Boolean} unsafeMode - enable to use `eval` to hydrate stringified functions based on `expectedTypeSchema` (this is not safe to use on user-provided input and so is disabled by default)
  29. * @return {*}
  30. */
  31. module.exports = function parseHuman (humanString, expectedTypeSchema, unsafeMode) {
  32. if (!_.isString(humanString)) {
  33. throw new Error('rttc.parseHuman() expects a string value, but a '+typeof humanString+' was provided: '+util.inspect(humanString, false, null));
  34. }
  35. if (unsafeMode && _.isUndefined(expectedTypeSchema)) {
  36. throw new Error('rttc.parseHuman() cannot enable `unsafeMode` without also providing a `expectedTypeSchema`.');
  37. }
  38. // If no type schema was specified, we will try to make a nice number or boolean
  39. // out of the value, but if that doesn't work, we'll leave it a string.
  40. if (_.isUndefined(expectedTypeSchema)) {
  41. try {
  42. return types.number.to(humanString);
  43. }
  44. catch (e){}
  45. try {
  46. return types.boolean.to(humanString);
  47. }
  48. catch (e){}
  49. return humanString;
  50. }
  51. // --•
  52. // A type schema was specified.
  53. // If the type schema is expecting a simple string, boolean, or number, then...
  54. if (expectedTypeSchema === 'string' || expectedTypeSchema === 'number' || expectedTypeSchema === 'boolean') {
  55. // Run the string through RTTC loose validation. and send the result back.
  56. // (if validation fails, an error will be thrown)
  57. return validate(expectedTypeSchema, humanString);
  58. }
  59. // --•
  60. // If the type schema is expecting a simple lamda function, attempt to use hydrate.
  61. // (but if `unsafeMode` is disabled, just return the string as-is)
  62. if (expectedTypeSchema === 'lamda') {
  63. if (!unsafeMode) { return humanString; }
  64. return hydrate(humanString, expectedTypeSchema);
  65. }
  66. // --•
  67. // Otherwise, we'll assume this was entered as JSON and parse it first...
  68. // ...and if we make it past that, then we'll validate (and potentially lightly coerce) the final result.
  69. return validate(expectedTypeSchema, parse(humanString, expectedTypeSchema, unsafeMode));
  70. };