1 | _ = require('lodash')
|
2 | _.str = require('underscore.string')
|
3 | async = require('async')
|
4 | Parameter = require('./parameter')
|
5 | settings = require('./settings')
|
6 | parse = require('./parse')
|
7 | utils = require('./utils')
|
8 |
|
9 | isLastOne = (parameters, predicate) ->
|
10 | lastParameter = _.last(parameters)
|
11 | return predicate(lastParameter)
|
12 |
|
13 | appearedMoreThanOnce = (parameters, predicate) ->
|
14 | filteredParameters = _.filter(parameters, predicate)
|
15 | return filteredParameters.length > 1
|
16 |
|
17 | module.exports = class Signature
|
18 | constructor: (signature) ->
|
19 |
|
20 | if not signature? or not _.isString(signature)
|
21 | throw new Error('Missing or invalid signature')
|
22 |
|
23 | @parameters = []
|
24 |
|
25 | _.each(parse.split(signature), @_addParameter, this)
|
26 |
|
27 | if @allowsStdin()
|
28 | isStdin = (parameter) ->
|
29 | return parameter.allowsStdin()
|
30 |
|
31 | if appearedMoreThanOnce(@parameters, isStdin)
|
32 | throw new Error('Signature can only contain one stdin parameter')
|
33 |
|
34 | if not isLastOne(@parameters, isStdin)
|
35 | throw new Error('The stdin parameter should be the last one')
|
36 |
|
37 | if @hasVariadicParameters()
|
38 | isVariadic = (parameter) ->
|
39 | return parameter.isVariadic()
|
40 |
|
41 | if appearedMoreThanOnce(@parameters, isVariadic)
|
42 | throw new Error('Signature can only contain one variadic parameter')
|
43 |
|
44 | if not isLastOne(@parameters, isVariadic)
|
45 | throw new Error('The variadic parameter should be the last one')
|
46 |
|
47 | _addParameter: (word) ->
|
48 | parameter = new Parameter(word)
|
49 | @parameters.push(parameter)
|
50 |
|
51 | hasParameters: ->
|
52 | return _.any @parameters, (parameter) ->
|
53 | return not parameter.isWord()
|
54 |
|
55 | hasVariadicParameters: ->
|
56 | return _.any @parameters, (parameter) ->
|
57 | return parameter.isVariadic()
|
58 |
|
59 | allowsStdin: ->
|
60 | return _.any @parameters, (parameter) ->
|
61 | return parameter.allowsStdin()
|
62 |
|
63 | toString: ->
|
64 | result = []
|
65 | for parameter in @parameters
|
66 | result.push(parameter.toString())
|
67 | return result.join(' ')
|
68 |
|
69 | isWildcard: ->
|
70 | return _.all [
|
71 | @parameters.length is 1
|
72 | @parameters[0].toString() is settings.signatures.wildcard
|
73 | ]
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | matches: (command, callback) ->
|
88 | @compileParameters command, (error) ->
|
89 | return callback(true) if not error?
|
90 |
|
91 | if _.str.startsWith(error.message, 'Missing')
|
92 | return callback(true)
|
93 | return callback(false)
|
94 | , false
|
95 |
|
96 | compileParameters: (command, callback, performStdin = true) ->
|
97 | commandWords = parse.split(command)
|
98 | comparison = _.zip(@parameters, commandWords)
|
99 |
|
100 | result = {}
|
101 |
|
102 | return callback(null, result) if @isWildcard()
|
103 |
|
104 | async.eachSeries comparison, (item, done) =>
|
105 | parameter = item[0]
|
106 | word = item[1]
|
107 |
|
108 | if not parameter?
|
109 | return callback(new Error('Signature dismatch'))
|
110 |
|
111 | parameterValue = parameter.getValue()
|
112 |
|
113 | if parameter.allowsStdin() and not word?
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | return callback(null, result) if not performStdin
|
120 |
|
121 | return utils.getStdin (stdin) ->
|
122 |
|
123 | if parameter.isRequired() and not stdin?
|
124 | return callback(new Error("Missing #{parameterValue}"))
|
125 |
|
126 | if stdin?
|
127 | result[parameterValue] = stdin
|
128 |
|
129 | return callback(null, result)
|
130 |
|
131 | if not parameter.matches(word)
|
132 | if parameter.isRequired()
|
133 | return callback(new Error("Missing #{parameterValue}"))
|
134 |
|
135 | return callback(new Error("#{parameterValue} does not match #{word}"))
|
136 |
|
137 | if parameter.isVariadic()
|
138 | parameterIndex = _.indexOf(@parameters, parameter)
|
139 | value = _.rest(commandWords, parameterIndex).join(' ')
|
140 |
|
141 | if parameter.isOptional() and _.isEmpty(value)
|
142 | return callback(null, result)
|
143 |
|
144 | result[parameterValue] = value
|
145 | return callback(null, result)
|
146 |
|
147 | if not parameter.isWord() and word?
|
148 | if /^\d+$/.test(word)
|
149 | result[parameterValue] = _.parseInt(word)
|
150 | else
|
151 | result[parameterValue] = word
|
152 |
|
153 | return done()
|
154 |
|
155 | , (error) ->
|
156 | return callback(error) if error?
|
157 | return callback(null, result)
|