toys-core 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ module Toys
31
+ class Tool
32
+ ##
33
+ # An Acceptor validates and converts arguments. It is designed to be
34
+ # compatible with the OptionParser accept mechanism.
35
+ #
36
+ # First, an acceptor validates an argument via the {#match} method. This
37
+ # method should determine whether the argument is valid, and return
38
+ # information that will help with conversion of the argument.
39
+ #
40
+ # Second, an acceptor converts the argument from the input string to its
41
+ # final form via the {#convert} method.
42
+ #
43
+ # Finally, an acceptor has a name that may appear in help text for flags
44
+ # and arguments that use it.
45
+ #
46
+ class Acceptor
47
+ ##
48
+ # Create a base acceptor.
49
+ #
50
+ # The basic acceptor does not do any validation (i.e. it accepts all
51
+ # arguments). You may subclass this object and implement the {#match}
52
+ # method to change this behavior.
53
+ #
54
+ # The converter should take one or more arguments, the first of which is
55
+ # the entire argument string, and the others of which may be returned
56
+ # from validation. The converter should return the final converted value
57
+ # of the argument. If you do not provide a converter, this acceptor will
58
+ # set the final value to the input argument string by default.
59
+ # You may provide a converter either as a proc or a block.
60
+ #
61
+ # @param [String] name A visible name for the acceptor, shown in help.
62
+ # @param [Proc] converter A converter function. May also be given as a
63
+ # block.
64
+ #
65
+ def initialize(name, converter = nil, &block)
66
+ @name = name
67
+ @converter = converter || block
68
+ end
69
+
70
+ ##
71
+ # Name of the acceptor
72
+ # @return [String]
73
+ #
74
+ attr_reader :name
75
+
76
+ ##
77
+ # Name of the acceptor as a string
78
+ # @return [String]
79
+ #
80
+ def to_s
81
+ name.to_s
82
+ end
83
+
84
+ ##
85
+ # Validate the given input.
86
+ #
87
+ # You may override this method to specify a validation function. For a
88
+ # valid input, the function must return either the original argument
89
+ # string, or an array of which the first element is the original argument
90
+ # string, and the remaining elements may comprise additional information.
91
+ # All returned information is then passed to the conversion function.
92
+ # Note that a MatchInfo object is a legitimate return value since it
93
+ # duck-types the appropriate array.
94
+ #
95
+ # For an invalid input, you should return a falsy value.
96
+ #
97
+ # The default implementation simply returns the original argument string,
98
+ # indicating all inputs are valid.
99
+ #
100
+ # @param [String] str Input argument string
101
+ # @return [String,Array]
102
+ #
103
+ def match(str)
104
+ str
105
+ end
106
+
107
+ ##
108
+ # Convert the given input.
109
+ #
110
+ # @param [String] str Original argument string
111
+ # @param [Object...] extra Zero or more additional arguments comprising
112
+ # additional elements returned from the match function.
113
+ # @return [Object] The converted argument as it should be stored in the
114
+ # context data.
115
+ #
116
+ def convert(str, *extra)
117
+ @converter ? @converter.call(str, *extra) : str
118
+ end
119
+ end
120
+
121
+ ##
122
+ # An acceptor that uses a regex to validate input.
123
+ #
124
+ class PatternAcceptor < Acceptor
125
+ ##
126
+ # Create an acceptor.
127
+ #
128
+ # You must provide a regular expression as a validator. You may also
129
+ # provide a converter.
130
+ #
131
+ # @param [String] name A visible name for the acceptor, shown in help.
132
+ # @param [Regexp] regex Regular expression defining value values.
133
+ # @param [Proc] converter A converter function. May also be given as a
134
+ # block. Note that the converter will be passed all elements of
135
+ # the MatchInfo.
136
+ #
137
+ def initialize(name, regex, converter = nil, &block)
138
+ super(name, converter, &block)
139
+ @regex = regex
140
+ end
141
+
142
+ ##
143
+ # Overrides match to match from the given regex.
144
+ #
145
+ def match(str)
146
+ @regex.match(str)
147
+ end
148
+ end
149
+
150
+ ##
151
+ # An acceptor that recognizes a fixed set of values.
152
+ #
153
+ # You provide a list of valid values. The input argument string will be
154
+ # matched against the string forms of these valid values. If it matches,
155
+ # the converter will return the actual value.
156
+ #
157
+ # For example, you could pass `[:one, :two, 3]` as the set of values. If
158
+ # an argument of `"two"` is passed in, the converter will yield a final
159
+ # value of the symbol `:two`. If an argument of "3" is passed in, the
160
+ # converter will yield the integer `3`. If an argument of "three" is
161
+ # passed in, the match will fail.
162
+ #
163
+ class EnumAcceptor < Acceptor
164
+ ##
165
+ # Create an acceptor.
166
+ #
167
+ # @param [String] name A visible name for the acceptor, shown in help.
168
+ # @param [Array] values Valid values.
169
+ #
170
+ def initialize(name, values)
171
+ super(name)
172
+ @values = Array(values).map { |v| [v.to_s, v] }
173
+ end
174
+
175
+ ##
176
+ # Overrides match to find the value.
177
+ #
178
+ def match(str)
179
+ @values.find { |s, _e| s == str }
180
+ end
181
+
182
+ ##
183
+ # Overrides convert to return the original element.
184
+ #
185
+ def convert(_str, elem)
186
+ elem
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,130 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ require "optparse"
31
+
32
+ require "toys/utils/wrappable_string"
33
+
34
+ module Toys
35
+ class Tool
36
+ ##
37
+ # Representation of a formal positional argument
38
+ #
39
+ class ArgDefinition
40
+ ##
41
+ # Create an ArgDefinition
42
+ # @private
43
+ #
44
+ def initialize(key, type, accept, default, desc, long_desc, display_name)
45
+ @key = key
46
+ @type = type
47
+ @accept = accept
48
+ @default = default
49
+ @desc = Utils::WrappableString.make(desc)
50
+ @long_desc = Utils::WrappableString.make_array(long_desc)
51
+ @display_name = display_name || key.to_s.tr("-", "_").gsub(/\W/, "").upcase
52
+ end
53
+
54
+ ##
55
+ # Returns the key.
56
+ # @return [Symbol]
57
+ #
58
+ attr_reader :key
59
+
60
+ ##
61
+ # Type of this argument.
62
+ # @return [:required,:optional,:remaining]
63
+ #
64
+ attr_reader :type
65
+
66
+ ##
67
+ # Returns the acceptor, which may be `nil`.
68
+ # @return [Object]
69
+ #
70
+ attr_accessor :accept
71
+
72
+ ##
73
+ # Returns the default value, which may be `nil`.
74
+ # @return [Object]
75
+ #
76
+ attr_reader :default
77
+
78
+ ##
79
+ # Returns the short description string.
80
+ # @return [Toys::Utils::WrappableString]
81
+ #
82
+ attr_reader :desc
83
+
84
+ ##
85
+ # Returns the long description strings as an array.
86
+ # @return [Array<Toys::Utils::WrappableString>]
87
+ #
88
+ attr_reader :long_desc
89
+
90
+ ##
91
+ # Returns the displayable name.
92
+ # @return [String]
93
+ #
94
+ attr_accessor :display_name
95
+
96
+ ##
97
+ # Process the given value through the acceptor.
98
+ # May raise an exception if the acceptor rejected the input.
99
+ #
100
+ # @param [String] input Input value
101
+ # @return [Object] Accepted value
102
+ #
103
+ def process_value(input)
104
+ return input unless accept
105
+ result = input
106
+ optparse = ::OptionParser.new
107
+ optparse.accept(accept) if accept.is_a?(Acceptor)
108
+ optparse.on("--abc=VALUE", accept) { |v| result = v }
109
+ optparse.parse(["--abc", input])
110
+ result
111
+ end
112
+
113
+ ##
114
+ # Set description
115
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc
116
+ #
117
+ def desc=(desc)
118
+ @desc = Utils::WrappableString.make(desc)
119
+ end
120
+
121
+ ##
122
+ # Set long description
123
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
124
+ #
125
+ def long_desc=(long_desc)
126
+ @long_desc = Utils::WrappableString.make_array(long_desc)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,322 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ require "optparse"
31
+
32
+ require "toys/utils/wrappable_string"
33
+
34
+ module Toys
35
+ class Tool
36
+ ##
37
+ # Representation of a single flag.
38
+ #
39
+ class FlagSyntax
40
+ ##
41
+ # Parse flag syntax
42
+ # @param [String] str syntax.
43
+ #
44
+ def initialize(str)
45
+ case str
46
+ when /^(-[\?\w])$/
47
+ setup(str, [$1], $1, "-", nil, nil, nil, nil)
48
+ when /^(-[\?\w])( ?)\[(\w+)\]$/
49
+ setup(str, [$1], $1, "-", :value, :optional, $2, $3)
50
+ when /^(-[\?\w])( ?)(\w+)$/
51
+ setup(str, [$1], $1, "-", :value, :required, $2, $3)
52
+ when /^--\[no-\](\w[\?\w-]*)$/
53
+ setup(str, ["--#{$1}", "--no-#{$1}"], str, "--", :boolean, nil, nil, nil)
54
+ when /^(--\w[\?\w-]*)$/
55
+ setup(str, [$1], $1, "--", nil, nil, nil, nil)
56
+ when /^(--\w[\?\w-]*)([= ])\[(\w+)\]$/
57
+ setup(str, [$1], $1, "--", :value, :optional, $2, $3)
58
+ when /^(--\w[\?\w-]*)([= ])(\w+)$/
59
+ setup(str, [$1], $1, "--", :value, :required, $2, $3)
60
+ else
61
+ raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
62
+ end
63
+ end
64
+
65
+ attr_reader :original_str
66
+ attr_reader :flags
67
+ attr_reader :str_without_value
68
+ attr_reader :flag_style
69
+ attr_reader :flag_type
70
+ attr_reader :value_type
71
+ attr_reader :value_delim
72
+ attr_reader :value_label
73
+ attr_reader :canonical_str
74
+
75
+ ## @private
76
+ def configure_canonical(canonical_flag_type, canonical_value_type,
77
+ canonical_value_label, canonical_value_delim)
78
+ return unless flag_type.nil?
79
+ @flag_type = canonical_flag_type
80
+ return unless canonical_flag_type == :value
81
+ @value_type = canonical_value_type
82
+ canonical_value_delim = "" if canonical_value_delim == "=" && flag_style == "-"
83
+ canonical_value_delim = "=" if canonical_value_delim == "" && flag_style == "--"
84
+ @value_delim = canonical_value_delim
85
+ @value_label = canonical_value_label
86
+ label = @value_type == :optional ? "[#{@value_label}]" : @value_label
87
+ @canonical_str = "#{str_without_value}#{@value_delim}#{label}"
88
+ end
89
+
90
+ private
91
+
92
+ def setup(original_str, flags, str_without_value, flag_style, flag_type, value_type,
93
+ value_delim, value_label)
94
+ @original_str = original_str
95
+ @flags = flags
96
+ @str_without_value = str_without_value
97
+ @flag_style = flag_style
98
+ @flag_type = flag_type
99
+ @value_type = value_type
100
+ @value_delim = value_delim
101
+ @value_label = value_label ? value_label.upcase : value_label
102
+ @canonical_str = original_str
103
+ end
104
+ end
105
+
106
+ ##
107
+ # Representation of a formal set of flags that set a particular context
108
+ # key. The flags within a single FlagDefinition are synonyms.
109
+ #
110
+ class FlagDefinition
111
+ ##
112
+ # The default handler replaces the previous value.
113
+ # @return [Proc]
114
+ #
115
+ DEFAULT_HANDLER = ->(val, _prev) { val }
116
+
117
+ ##
118
+ # Create a FlagDefinition
119
+ # @private
120
+ #
121
+ def initialize(key, flags, used_flags, report_collisions, accept, handler, default)
122
+ @key = key
123
+ @flag_syntax = flags.map { |s| FlagSyntax.new(s) }
124
+ @accept = accept
125
+ @handler = handler || DEFAULT_HANDLER
126
+ @desc = Utils::WrappableString.make(desc)
127
+ @long_desc = Utils::WrappableString.make_array(long_desc)
128
+ @default = default
129
+ needs_val = (!accept.nil? && accept != ::TrueClass && accept != ::FalseClass) ||
130
+ (!default.nil? && default != true && default != false)
131
+ create_default_flag_if_needed(needs_val)
132
+ remove_used_flags(used_flags, report_collisions)
133
+ canonicalize(needs_val)
134
+ end
135
+
136
+ ##
137
+ # Returns the key.
138
+ # @return [Symbol]
139
+ #
140
+ attr_reader :key
141
+
142
+ ##
143
+ # Returns an array of FlagSyntax for the flags.
144
+ # @return [Array<FlagSyntax>]
145
+ #
146
+ attr_reader :flag_syntax
147
+
148
+ ##
149
+ # Returns the acceptor, which may be `nil`.
150
+ # @return [Object]
151
+ #
152
+ attr_reader :accept
153
+
154
+ ##
155
+ # Returns the default value, which may be `nil`.
156
+ # @return [Object]
157
+ #
158
+ attr_reader :default
159
+
160
+ ##
161
+ # Returns the short description string.
162
+ # @return [Toys::Utils::WrappableString]
163
+ #
164
+ attr_reader :desc
165
+
166
+ ##
167
+ # Returns the long description strings as an array.
168
+ # @return [Array<Toys::Utils::WrappableString>]
169
+ #
170
+ attr_reader :long_desc
171
+
172
+ ##
173
+ # Returns the handler for setting/updating the value.
174
+ # @return [Proc]
175
+ #
176
+ attr_reader :handler
177
+
178
+ ##
179
+ # The type of flag. Possible values are `:boolean` for a simple boolean
180
+ # switch, or `:value` for a flag that sets a value.
181
+ # @return [:boolean,:value]
182
+ #
183
+ attr_reader :flag_type
184
+
185
+ ##
186
+ # The type of value. Set to `:required` or `:optional` if the flag type
187
+ # is `:value`. Otherwise set to `nil`.
188
+ # @return [:required,:optional,nil]
189
+ #
190
+ attr_reader :value_type
191
+
192
+ ##
193
+ # The string label for the value as it should display in help, or `nil`
194
+ # if the flag type is not `:value`.
195
+ # @return [String,nil]
196
+ #
197
+ attr_reader :value_label
198
+
199
+ ##
200
+ # The value delimiter, which may be `""`, `" "`, or `"="`. Set to `nil`
201
+ # if the flag type is not `:value`.
202
+ # @return [String,nil]
203
+ #
204
+ attr_reader :value_delim
205
+
206
+ ##
207
+ # Returns an array of FlagSyntax including only single-dash flags
208
+ # @return [Array<FlagSyntax>]
209
+ #
210
+ def single_flag_syntax
211
+ @single_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "-" }
212
+ end
213
+
214
+ ##
215
+ # Returns an array of FlagSyntax including only double-dash flags
216
+ # @return [Array<FlagSyntax>]
217
+ #
218
+ def double_flag_syntax
219
+ @double_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "--" }
220
+ end
221
+
222
+ ##
223
+ # Returns the list of effective flags used.
224
+ # @return [Array<String>]
225
+ #
226
+ def effective_flags
227
+ @effective_flags ||= flag_syntax.map(&:flags).flatten
228
+ end
229
+
230
+ ##
231
+ # All optparser flags and acceptor if present
232
+ # @return [Array]
233
+ #
234
+ def optparser_info
235
+ @optparser_info ||= flag_syntax.map(&:canonical_str) + Array(accept)
236
+ end
237
+
238
+ ##
239
+ # Returns true if this flag is active. That is, it has a nonempty
240
+ # flags list.
241
+ # @return [Boolean]
242
+ #
243
+ def active?
244
+ !effective_flags.empty?
245
+ end
246
+
247
+ ##
248
+ # Set description
249
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc
250
+ #
251
+ def desc=(desc)
252
+ @desc = Utils::WrappableString.make(desc)
253
+ end
254
+
255
+ ##
256
+ # Set long description
257
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
258
+ #
259
+ def long_desc=(long_desc)
260
+ @long_desc = Utils::WrappableString.make_array(long_desc)
261
+ end
262
+
263
+ private
264
+
265
+ def create_default_flag_if_needed(needs_val)
266
+ return unless @flag_syntax.empty?
267
+ canonical_flag = key.to_s.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
268
+ unless canonical_flag.empty?
269
+ flag = needs_val ? "--#{canonical_flag} VALUE" : "--#{canonical_flag}"
270
+ @flag_syntax << FlagSyntax.new(flag)
271
+ end
272
+ end
273
+
274
+ def remove_used_flags(used_flags, report_collisions)
275
+ @flag_syntax.select! do |fs|
276
+ fs.flags.all? do |f|
277
+ collision = used_flags.include?(f)
278
+ if collision && report_collisions
279
+ raise ToolDefinitionError,
280
+ "Cannot use flag #{f.inspect} because it is already assigned or reserved."
281
+ end
282
+ !collision
283
+ end
284
+ end
285
+ used_flags.concat(effective_flags.uniq)
286
+ end
287
+
288
+ def canonicalize(needs_val)
289
+ @flag_type = needs_val ? :value : nil
290
+ @value_type = nil
291
+ @value_label = needs_val ? "VALUE" : nil
292
+ @value_delim = " "
293
+ single_flag_syntax.each do |flag|
294
+ analyze_flag_syntax(flag)
295
+ end
296
+ double_flag_syntax.each do |flag|
297
+ analyze_flag_syntax(flag)
298
+ end
299
+ @flag_type ||= :boolean
300
+ flag_syntax.each do |flag|
301
+ flag.configure_canonical(@flag_type, @value_type, @value_label, @value_delim)
302
+ end
303
+ end
304
+
305
+ def analyze_flag_syntax(flag)
306
+ return if flag.flag_type.nil?
307
+ if !@flag_type.nil? && @flag_type != flag.flag_type
308
+ raise ToolDefinitionError, "Cannot have both value and boolean flags for #{key.inspect}"
309
+ end
310
+ @flag_type = flag.flag_type
311
+ return unless @flag_type == :value
312
+ if !@value_type.nil? && @value_type != flag.value_type
313
+ raise ToolDefinitionError,
314
+ "Cannot have both required and optional values for flag #{key.inspect}"
315
+ end
316
+ @value_type = flag.value_type
317
+ @value_label = flag.value_label
318
+ @value_delim = flag.value_delim
319
+ end
320
+ end
321
+ end
322
+ end