UNPKG

4.54 kBtext/coffeescriptView Raw
1_ = require('lodash')
2_.str = require('underscore.string')
3async = require('async')
4Parameter = require('./parameter')
5settings = require('./settings')
6parse = require('./parse')
7utils = require('./utils')
8
9isLastOne = (parameters, predicate) ->
10 lastParameter = _.last(parameters)
11 return predicate(lastParameter)
12
13appearedMoreThanOnce = (parameters, predicate) ->
14 filteredParameters = _.filter(parameters, predicate)
15 return filteredParameters.length > 1
16
17module.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 # TODO: There should be a better way to implement
76 # this algorithm without duplicating a big chunk of
77 # compileParameters().
78 # Maybe there should be a third function that these
79 # two algorithms share?
80 # Related to this issue, if a command accepts input from stdin
81 # matches() calls compileParameters() thus causing compileParameters()
82 # to be called twice per execution, one for testing the match command
83 # and another one to actually do the real compilation.
84 # Stdin can be grabbed once, so the result is grabbed by matches()
85 # and when compileParameters() tries to fetch stdin again, there's
86 # nothing else to retrieve.
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 # Used to prevent matches() to retrieve the
116 # input from stdin.
117 # TODO: This should be unnecessary once matches()
118 # do not calls compileParameters() anymore.
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)