opt.coffee 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. fs = require 'fs'
  2. Q = require 'q'
  3. Color = require('./color').Color
  4. Cmd = require('./cmd').Cmd
  5. ###*
  6. Option
  7. Named entity. Options may have short and long keys for use from command line.
  8. @namespace
  9. @class Presents option
  10. ###
  11. exports.Opt = class Opt
  12. ###*
  13. @constructs
  14. @param {COA.Cmd} cmd parent command
  15. ###
  16. constructor: (@_cmd) -> @_cmd._opts.push @
  17. ###*
  18. Set a canonical option identifier to be used anywhere in the API.
  19. @param {String} _name option name
  20. @returns {COA.Opt} this instance (for chainability)
  21. ###
  22. name: (@_name) -> @
  23. ###*
  24. Set a long description for option to be used anywhere in text messages.
  25. @param {String} _title option title
  26. @returns {COA.Opt} this instance (for chainability)
  27. ###
  28. title: Cmd::title
  29. ###*
  30. Set a short key for option to be used with one hyphen from command line.
  31. @param {String} _short
  32. @returns {COA.Opt} this instance (for chainability)
  33. ###
  34. short: (@_short) -> @_cmd._optsByKey['-' + _short] = @
  35. ###*
  36. Set a short key for option to be used with double hyphens from command line.
  37. @param {String} _long
  38. @returns {COA.Opt} this instance (for chainability)
  39. ###
  40. long: (@_long) -> @_cmd._optsByKey['--' + _long] = @
  41. ###*
  42. Make an option boolean, i.e. option without value.
  43. @returns {COA.Opt} this instance (for chainability)
  44. ###
  45. flag: () ->
  46. @_flag = true
  47. @
  48. ###*
  49. Makes an option accepts multiple values.
  50. Otherwise, the value will be used by the latter passed.
  51. @returns {COA.Opt} this instance (for chainability)
  52. ###
  53. arr: ->
  54. @_arr = true
  55. @
  56. ###*
  57. Makes an option required.
  58. @returns {COA.Opt} this instance (for chainability)
  59. ###
  60. req: ->
  61. @_req = true
  62. @
  63. ###*
  64. Makes an option to act as a command,
  65. i.e. program will exit just after option action.
  66. @returns {COA.Opt} this instance (for chainability)
  67. ###
  68. only: ->
  69. @_only = true
  70. @
  71. ###*
  72. Set a validation (or value) function for option.
  73. Value from command line passes through before becoming available from API.
  74. Using for validation and convertion simple types to any values.
  75. @param {Function} _val validating function,
  76. invoked in the context of option instance
  77. and has one parameter with value from command line
  78. @returns {COA.Opt} this instance (for chainability)
  79. ###
  80. val: (@_val) -> @
  81. ###*
  82. Set a default value for option.
  83. Default value passed through validation function as ordinary value.
  84. @param {Object} _def
  85. @returns {COA.Opt} this instance (for chainability)
  86. ###
  87. def: (@_def) -> @
  88. ###*
  89. Make option value inputting stream.
  90. It's add useful validation and shortcut for STDIN.
  91. @returns {COA.Opt} this instance (for chainability)
  92. ###
  93. input: ->
  94. # XXX: hack to workaround a bug in node 0.6.x,
  95. # see https://github.com/joyent/node/issues/2130
  96. process.stdin.pause();
  97. @
  98. .def(process.stdin)
  99. .val (v) ->
  100. if typeof v is 'string'
  101. if v is '-'
  102. process.stdin
  103. else
  104. s = fs.createReadStream v, { encoding: 'utf8' }
  105. s.pause()
  106. s
  107. else v
  108. ###*
  109. Make option value outputing stream.
  110. It's add useful validation and shortcut for STDOUT.
  111. @returns {COA.Opt} this instance (for chainability)
  112. ###
  113. output: ->
  114. @
  115. .def(process.stdout)
  116. .val (v) ->
  117. if typeof v is 'string'
  118. if v is '-'
  119. process.stdout
  120. else
  121. fs.createWriteStream v, { encoding: 'utf8' }
  122. else v
  123. ###*
  124. Add action for current option command.
  125. This action is performed if the current option
  126. is present in parsed options (with any value).
  127. @param {Function} act action function,
  128. invoked in the context of command instance
  129. and has the parameters:
  130. - {Object} opts parsed options
  131. - {Array} args parsed arguments
  132. - {Object} res actions result accumulator
  133. It can return rejected promise by Cmd.reject (in case of error)
  134. or any other value treated as result.
  135. @returns {COA.Opt} this instance (for chainability)
  136. ###
  137. act: (act) ->
  138. opt = @
  139. name = @_name
  140. @_cmd.act (opts) ->
  141. if name of opts
  142. res = act.apply @, arguments
  143. if opt._only
  144. Q.when res, (res) =>
  145. @reject {
  146. toString: -> res.toString()
  147. exitCode: 0
  148. }
  149. else
  150. res
  151. @
  152. ###*
  153. Set custom additional completion for current option.
  154. @param {Function} completion generation function,
  155. invoked in the context of option instance.
  156. Accepts parameters:
  157. - {Object} opts completion options
  158. It can return promise or any other value treated as result.
  159. @returns {COA.Opt} this instance (for chainability)
  160. ###
  161. comp: Cmd::comp
  162. _saveVal: (opts, val) ->
  163. if @_val then val = @_val val
  164. if @_arr
  165. (opts[@_name] or= []).push val
  166. else
  167. opts[@_name] = val
  168. val
  169. _parse: (argv, opts) ->
  170. @_saveVal(
  171. opts,
  172. if @_flag
  173. true
  174. else
  175. argv.shift()
  176. )
  177. _checkParsed: (opts, args) -> not opts.hasOwnProperty @_name
  178. _usage: ->
  179. res = []
  180. nameStr = @_name.toUpperCase()
  181. if @_short
  182. res.push '-', Color 'lgreen', @_short
  183. unless @_flag then res.push ' ' + nameStr
  184. res.push ', '
  185. if @_long
  186. res.push '--', Color 'green', @_long
  187. unless @_flag then res.push '=' + nameStr
  188. res.push ' : ', @_title
  189. if @_req then res.push ' ', Color('lred', '(required)')
  190. res.join ''
  191. _requiredText: -> 'Missing required option:\n ' + @_usage()
  192. ###*
  193. Return rejected promise with error code.
  194. Use in .val() for return with error.
  195. @param {Object} reject reason
  196. You can customize toString() method and exitCode property
  197. of reason object.
  198. @returns {Q.promise} rejected promise
  199. ###
  200. reject: Cmd::reject
  201. ###*
  202. Finish chain for current option and return parent command instance.
  203. @returns {COA.Cmd} parent command
  204. ###
  205. end: Cmd::end
  206. ###*
  207. Apply function with arguments in context of option instance.
  208. @param {Function} fn
  209. @param {Array} args
  210. @returns {COA.Opt} this instance (for chainability)
  211. ###
  212. apply: Cmd::apply