cli-kit 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cla.yml +22 -0
  3. data/.github/workflows/ruby.yml +34 -2
  4. data/.gitignore +2 -0
  5. data/.rubocop.sorbet.yml +47 -0
  6. data/.rubocop.yml +16 -1
  7. data/Gemfile +10 -1
  8. data/Gemfile.lock +94 -18
  9. data/README.md +46 -3
  10. data/Rakefile +1 -0
  11. data/bin/onchange +30 -0
  12. data/bin/tapioca +29 -0
  13. data/bin/testunit +1 -0
  14. data/cli-kit.gemspec +2 -2
  15. data/dev.yml +35 -3
  16. data/examples/minimal/example.rb +3 -1
  17. data/examples/single-file/example.rb +25 -35
  18. data/gen/lib/gen/commands/help.rb +8 -10
  19. data/gen/lib/gen/commands/new.rb +23 -9
  20. data/gen/lib/gen/commands.rb +21 -9
  21. data/gen/lib/gen/entry_point.rb +12 -3
  22. data/gen/lib/gen/generator.rb +28 -7
  23. data/gen/lib/gen/help.rb +63 -0
  24. data/gen/lib/gen.rb +18 -23
  25. data/gen/template/bin/update-deps +2 -2
  26. data/gen/template/lib/__app__/commands.rb +1 -4
  27. data/gen/template/lib/__app__.rb +8 -17
  28. data/gen/template/test/example_test.rb +1 -1
  29. data/lib/cli/kit/args/definition.rb +344 -0
  30. data/lib/cli/kit/args/evaluation.rb +245 -0
  31. data/lib/cli/kit/args/parser/node.rb +132 -0
  32. data/lib/cli/kit/args/parser.rb +129 -0
  33. data/lib/cli/kit/args/tokenizer.rb +133 -0
  34. data/lib/cli/kit/args.rb +16 -0
  35. data/lib/cli/kit/base_command.rb +17 -32
  36. data/lib/cli/kit/command_help.rb +271 -0
  37. data/lib/cli/kit/command_registry.rb +69 -17
  38. data/lib/cli/kit/config.rb +25 -22
  39. data/lib/cli/kit/core_ext.rb +30 -0
  40. data/lib/cli/kit/error_handler.rb +131 -70
  41. data/lib/cli/kit/executor.rb +19 -3
  42. data/lib/cli/kit/ini.rb +31 -38
  43. data/lib/cli/kit/levenshtein.rb +12 -4
  44. data/lib/cli/kit/logger.rb +16 -2
  45. data/lib/cli/kit/opts.rb +301 -0
  46. data/lib/cli/kit/resolver.rb +8 -0
  47. data/lib/cli/kit/sorbet_runtime_stub.rb +156 -0
  48. data/lib/cli/kit/support/test_helper.rb +23 -14
  49. data/lib/cli/kit/support.rb +2 -0
  50. data/lib/cli/kit/system.rb +188 -54
  51. data/lib/cli/kit/util.rb +48 -103
  52. data/lib/cli/kit/version.rb +3 -1
  53. data/lib/cli/kit.rb +103 -7
  54. metadata +22 -10
  55. data/.github/probots.yml +0 -2
  56. data/lib/cli/kit/autocall.rb +0 -21
  57. data/lib/cli/kit/ruby_backports/enumerable.rb +0 -6
@@ -0,0 +1,344 @@
1
+ # typed: true
2
+
3
+ require 'cli/kit'
4
+
5
+ module CLI
6
+ module Kit
7
+ module Args
8
+ class Definition
9
+ extend T::Sig
10
+
11
+ Error = Class.new(Args::Error)
12
+ ConflictingFlag = Class.new(Error)
13
+ InvalidFlag = Class.new(Error)
14
+ InvalidLookup = Class.new(Error)
15
+ InvalidPosition = Class.new(Error)
16
+
17
+ sig { returns(T::Array[Flag]) }
18
+ attr_reader :flags
19
+
20
+ sig { returns(T::Array[Option]) }
21
+ attr_reader :options
22
+
23
+ sig { returns(T::Array[Position]) }
24
+ attr_reader :positions
25
+
26
+ sig { params(name: Symbol, short: T.nilable(String), long: T.nilable(String), desc: T.nilable(String)).void }
27
+ def add_flag(name, short: nil, long: nil, desc: nil)
28
+ short, long = strip_prefixes_and_validate(short, long)
29
+ flag = Flag.new(name: name, short: short, long: long, desc: desc)
30
+ add_resolution(flag)
31
+ @flags << flag
32
+ end
33
+
34
+ sig do
35
+ params(
36
+ name: Symbol, short: T.nilable(String), long: T.nilable(String),
37
+ desc: T.nilable(String),
38
+ default: T.any(
39
+ NilClass,
40
+ String, T.proc.returns(String),
41
+ T::Array[String], T.proc.returns(T::Array[String])
42
+ ),
43
+ required: T::Boolean, multi: T::Boolean
44
+ ).void
45
+ end
46
+ def add_option(name, short: nil, long: nil, desc: nil, default: nil, required: false, multi: false)
47
+ short, long = strip_prefixes_and_validate(short, long)
48
+ option = Option.new(
49
+ name: name, short: short, long: long, desc: desc, default: default,
50
+ required: required, multi: multi
51
+ )
52
+ add_resolution(option)
53
+ @options << option
54
+ end
55
+
56
+ sig do
57
+ params(
58
+ name: Symbol,
59
+ required: T::Boolean,
60
+ multi: T::Boolean,
61
+ desc: T.nilable(String),
62
+ default: T.any(NilClass, String, T.proc.returns(String)),
63
+ skip: T.any(
64
+ NilClass,
65
+ T.proc.returns(T::Boolean),
66
+ T.proc.params(arg0: String).returns(T::Boolean),
67
+ ),
68
+ ).void
69
+ end
70
+ def add_position(name, required:, multi:, desc: nil, default: nil, skip: nil)
71
+ position = Position.new(
72
+ name: name, desc: desc, required: required, multi: multi,
73
+ default: default, skip: skip
74
+ )
75
+ validate_order(position)
76
+ add_name_resolution(position)
77
+ @positions << position
78
+ end
79
+
80
+ sig { void }
81
+ def initialize
82
+ @flags = []
83
+ @options = []
84
+ @by_short = {}
85
+ @by_long = {}
86
+ @by_name = {}
87
+ @positions = []
88
+ end
89
+
90
+ module OptBase
91
+ extend T::Sig
92
+
93
+ sig { returns(Symbol) }
94
+ attr_reader :name
95
+
96
+ sig { returns(T.nilable(String)) }
97
+ attr_reader :desc
98
+ end
99
+
100
+ module OptValue
101
+ extend T::Sig
102
+
103
+ sig { returns(T.any(NilClass, String, T::Array[String])) }
104
+ def default
105
+ if @default.is_a?(Proc)
106
+ @default.call
107
+ else
108
+ @default
109
+ end
110
+ end
111
+
112
+ sig { returns(T::Boolean) }
113
+ def dynamic_default?
114
+ @default.is_a?(Proc)
115
+ end
116
+
117
+ sig { returns(T::Boolean) }
118
+ def required?
119
+ @required
120
+ end
121
+
122
+ sig { returns(T::Boolean) }
123
+ def multi?
124
+ @multi
125
+ end
126
+
127
+ sig { returns(T::Boolean) }
128
+ def optional?
129
+ !required?
130
+ end
131
+ end
132
+
133
+ class Flag
134
+ extend T::Sig
135
+ include OptBase
136
+
137
+ sig { returns(T.nilable(String)) }
138
+ attr_reader :short
139
+
140
+ sig { returns(T.nilable(String)) }
141
+ attr_reader :long
142
+
143
+ sig { returns(String) }
144
+ def as_written_by_user
145
+ long ? "--#{long}" : "-#{short}"
146
+ end
147
+
148
+ sig { params(name: Symbol, short: T.nilable(String), long: T.nilable(String), desc: T.nilable(String)).void }
149
+ def initialize(name:, short: nil, long: nil, desc: nil)
150
+ if long&.start_with?('-') || short&.start_with?('-')
151
+ raise(ArgumentError, 'invalid - prefix')
152
+ end
153
+
154
+ @name = name
155
+ @short = short
156
+ @long = long
157
+ @desc = desc
158
+ end
159
+ end
160
+
161
+ class Position
162
+ extend T::Sig
163
+ include OptBase
164
+ include OptValue
165
+
166
+ sig do
167
+ params(
168
+ name: Symbol,
169
+ desc: T.nilable(String),
170
+ required: T::Boolean,
171
+ multi: T::Boolean,
172
+ default: T.any(NilClass, String, T.proc.returns(String)),
173
+ skip: T.any(
174
+ NilClass,
175
+ T.proc.returns(T::Boolean),
176
+ T.proc.params(arg0: String).returns(T::Boolean),
177
+ ),
178
+ ).void
179
+ end
180
+ def initialize(name:, desc:, required:, multi:, default: nil, skip: nil)
181
+ if multi && (default || required)
182
+ raise(ArgumentError, 'multi-valued positions cannot have a default or required value')
183
+ end
184
+
185
+ @name = name
186
+ @desc = desc
187
+ @required = required
188
+ @multi = multi
189
+ @default = default
190
+ @skip = skip
191
+ end
192
+
193
+ sig { params(arg: String).returns(T::Boolean) }
194
+ def skip?(arg)
195
+ if @skip.nil?
196
+ false
197
+ elsif T.must(@skip).arity == 0
198
+ T.cast(@skip, T.proc.returns(T::Boolean)).call
199
+ else
200
+ T.cast(@skip, T.proc.params(arg0: String).returns(T::Boolean)).call(arg)
201
+ end
202
+ end
203
+ end
204
+
205
+ class Option < Flag
206
+ extend T::Sig
207
+ include OptValue
208
+
209
+ sig do
210
+ params(
211
+ name: Symbol, short: T.nilable(String), long: T.nilable(String),
212
+ desc: T.nilable(String),
213
+ default: T.any(
214
+ NilClass,
215
+ String, T.proc.returns(String),
216
+ T::Array[String], T.proc.returns(T::Array[String])
217
+ ),
218
+ required: T::Boolean, multi: T::Boolean
219
+ ).void
220
+ end
221
+ def initialize(name:, short: nil, long: nil, desc: nil, default: nil, required: false, multi: false)
222
+ if multi && required
223
+ raise(ArgumentError, 'multi-valued options cannot have a required value')
224
+ end
225
+
226
+ super(name: name, short: short, long: long, desc: desc)
227
+ @default = default
228
+ @required = required
229
+ @multi = multi
230
+ end
231
+ end
232
+
233
+ sig { params(name: Symbol).returns(T.nilable(Flag)) }
234
+ def lookup_flag(name)
235
+ flagopt = @by_name[name]
236
+ if flagopt.class == Flag
237
+ flagopt
238
+ end
239
+ end
240
+
241
+ sig { params(name: Symbol).returns(T.nilable(Option)) }
242
+ def lookup_option(name)
243
+ flagopt = @by_name[name]
244
+ if flagopt.class == Option
245
+ flagopt
246
+ end
247
+ end
248
+
249
+ sig { params(name: String).returns(T.any(Flag, Option, NilClass)) }
250
+ def lookup_short(name)
251
+ raise(InvalidLookup, "invalid '-' prefix") if name.start_with?('-')
252
+
253
+ @by_short[name]
254
+ end
255
+
256
+ sig { params(name: String).returns(T.any(Flag, Option, NilClass)) }
257
+ def lookup_long(name)
258
+ raise(InvalidLookup, "invalid '-' prefix") if name.start_with?('-')
259
+
260
+ @by_long[name]
261
+ end
262
+
263
+ sig { params(name: Symbol).returns(T.nilable(Position)) }
264
+ def lookup_position(name)
265
+ position = @by_name[name]
266
+ if position.class == Position
267
+ position
268
+ end
269
+ end
270
+
271
+ private
272
+
273
+ sig { params(position: Position).void }
274
+ def validate_order(position)
275
+ raise(InvalidPosition, 'Cannot have any more positional arguments after multi') if @positions.last&.multi?
276
+ end
277
+
278
+ sig { params(short: String).returns(String) }
279
+ def strip_short_prefix(short)
280
+ unless short.match?(/^-[^-]/)
281
+ raise(InvalidFlag, "Short flag '#{short}' does not start with '-'")
282
+ end
283
+ if short.size != 2
284
+ raise(InvalidFlag, 'Short flag must be a single character')
285
+ end
286
+
287
+ short.sub(/^-/, '')
288
+ end
289
+
290
+ sig { params(long: String).returns(String) }
291
+ def strip_long_prefix(long)
292
+ unless long.match?(/^--[^-]/)
293
+ raise(InvalidFlag, "Long flag '#{long}' does not start with '--'")
294
+ end
295
+
296
+ long.sub(/^--/, '')
297
+ end
298
+
299
+ sig do
300
+ params(short: T.nilable(String), long: T.nilable(String))
301
+ .returns([T.nilable(String), T.nilable(String)])
302
+ end
303
+ def strip_prefixes_and_validate(short, long)
304
+ if short.nil? && long.nil?
305
+ raise(Error, 'One or more of short and long must be specified')
306
+ end
307
+
308
+ short = strip_short_prefix(short) if short
309
+ long = strip_long_prefix(long) if long
310
+
311
+ [short, long]
312
+ end
313
+
314
+ sig { params(flagopt: Flag).void }
315
+ def add_resolution(flagopt)
316
+ if flagopt.short
317
+ if (existing = @by_short[flagopt.short])
318
+ raise(ConflictingFlag, "Short flag '#{flagopt.short}' already defined by #{existing.name}")
319
+ end
320
+
321
+ @by_short[flagopt.short] = flagopt
322
+ end
323
+ if flagopt.long
324
+ if (existing = @by_long[flagopt.long])
325
+ raise(ConflictingFlag, "Long flag '#{flagopt.long}' already defined by #{existing.name}")
326
+ end
327
+
328
+ @by_long[flagopt.long] = flagopt
329
+ end
330
+ add_name_resolution(flagopt)
331
+ end
332
+
333
+ sig { params(arg: T.any(Flag, Position)).void }
334
+ def add_name_resolution(arg)
335
+ if (existing = @by_name[arg.name])
336
+ raise(ConflictingFlag, "Flag '#{arg.name}' already defined by #{existing.name}")
337
+ end
338
+
339
+ @by_name[arg.name] = arg
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,245 @@
1
+ # typed: true
2
+
3
+ require 'cli/kit'
4
+
5
+ module CLI
6
+ module Kit
7
+ module Args
8
+ class Evaluation
9
+ extend T::Sig
10
+
11
+ Error = Class.new(Args::Error)
12
+
13
+ class MissingRequiredOption < Error
14
+ extend T::Sig
15
+ sig { params(name: String).void }
16
+ def initialize(name)
17
+ super("missing required option `#{name}'")
18
+ end
19
+ end
20
+
21
+ class MissingRequiredPosition < Error
22
+ extend T::Sig
23
+ sig { void }
24
+ def initialize
25
+ super('more arguments required')
26
+ end
27
+ end
28
+
29
+ class TooManyPositions < Error
30
+ extend T::Sig
31
+ sig { void }
32
+ def initialize
33
+ super('too many arguments')
34
+ end
35
+ end
36
+
37
+ class FlagProxy
38
+ extend T::Sig
39
+
40
+ sig { params(sym: Symbol).returns(T::Boolean) }
41
+ def method_missing(sym)
42
+ flag = @evaluation.defn.lookup_flag(sym)
43
+ unless flag
44
+ raise NoMethodError, "undefined flag `#{sym}' for #{self}"
45
+ end
46
+
47
+ @evaluation.send(:lookup_flag, flag)
48
+ end
49
+
50
+ sig { params(sym: Symbol, include_private: T::Boolean).returns(T::Boolean) }
51
+ def respond_to_missing?(sym, include_private = false)
52
+ !!@evaluation.defn.lookup_flag(sym)
53
+ end
54
+
55
+ sig { params(evaluation: Evaluation).void }
56
+ def initialize(evaluation)
57
+ @evaluation = evaluation
58
+ end
59
+ end
60
+
61
+ class OptionProxy
62
+ extend T::Sig
63
+
64
+ sig { params(sym: Symbol).returns(T.any(NilClass, String, T::Array[String])) }
65
+ def method_missing(sym)
66
+ opt = @evaluation.defn.lookup_option(sym)
67
+ unless opt
68
+ raise NoMethodError, "undefined option `#{sym}' for #{self}"
69
+ end
70
+
71
+ @evaluation.send(:lookup_option, opt)
72
+ end
73
+
74
+ sig { params(sym: Symbol, include_private: T::Boolean).returns(T::Boolean) }
75
+ def respond_to_missing?(sym, include_private = false)
76
+ !!@evaluation.defn.lookup_option(sym)
77
+ end
78
+
79
+ sig { params(evaluation: Evaluation).void }
80
+ def initialize(evaluation)
81
+ @evaluation = evaluation
82
+ end
83
+ end
84
+
85
+ class PositionProxy
86
+ extend T::Sig
87
+
88
+ sig { params(sym: Symbol).returns(T.any(NilClass, String, T::Array[String])) }
89
+ def method_missing(sym)
90
+ position = @evaluation.defn.lookup_position(sym)
91
+ unless position
92
+ raise NoMethodError, "undefined position `#{sym}' for #{self}"
93
+ end
94
+
95
+ @evaluation.send(:lookup_position, position)
96
+ end
97
+
98
+ sig { params(sym: Symbol, include_private: T::Boolean).returns(T::Boolean) }
99
+ def respond_to_missing?(sym, include_private = false)
100
+ !!@evaluation.defn.lookup_position(sym)
101
+ end
102
+
103
+ sig { params(evaluation: Evaluation).void }
104
+ def initialize(evaluation)
105
+ @evaluation = evaluation
106
+ end
107
+ end
108
+
109
+ sig { returns(FlagProxy) }
110
+ def flag
111
+ @flag_proxy ||= FlagProxy.new(self)
112
+ end
113
+
114
+ sig { returns(OptionProxy) }
115
+ def opt
116
+ @option_proxy ||= OptionProxy.new(self)
117
+ end
118
+
119
+ sig { returns(PositionProxy) }
120
+ def position
121
+ @position_proxy ||= PositionProxy.new(self)
122
+ end
123
+
124
+ sig { returns(Definition) }
125
+ attr_reader :defn
126
+
127
+ sig { returns(T::Array[Parser::Node]) }
128
+ attr_reader :parse
129
+
130
+ sig { returns(T::Array[String]) }
131
+ def unparsed
132
+ @unparsed ||= begin
133
+ nodes = T.cast(
134
+ parse.select { |node| node.is_a?(Parser::Node::Unparsed) },
135
+ T::Array[Parser::Node::Unparsed],
136
+ )
137
+ nodes.flat_map(&:value)
138
+ end
139
+ end
140
+
141
+ sig { params(defn: Definition, parse: T::Array[Parser::Node]).void }
142
+ def initialize(defn, parse)
143
+ @defn = defn
144
+ @parse = parse
145
+ check_required_options!
146
+ end
147
+
148
+ sig { void }
149
+ def check_required_options!
150
+ @defn.options.each do |opt|
151
+ next unless opt.required?
152
+
153
+ node = @parse.detect do |node|
154
+ node.is_a?(Parser::Node::Option) && node.name == opt.name
155
+ end
156
+ if !node || T.cast(node, Parser::Node::Option).value.nil?
157
+ raise(MissingRequiredOption, opt.as_written_by_user)
158
+ end
159
+ end
160
+ end
161
+
162
+ sig { void }
163
+ def resolve_positions!
164
+ args_i = 0
165
+ @position_values = Hash.new
166
+ @defn.positions.each do |position|
167
+ raise(MissingRequiredPosition) if position.required? && args_i >= args.size
168
+ next if args_i >= args.size || position.skip?(T.must(args[args_i]))
169
+
170
+ if position.multi?
171
+ @position_values[position.name] = args[args_i..]
172
+ args_i = args.size
173
+ else
174
+ @position_values[position.name] = T.must(args[args_i])
175
+ args_i += 1
176
+ end
177
+ end
178
+ raise(TooManyPositions) if args_i < args.size
179
+ end
180
+
181
+ sig { params(flag: Definition::Flag).returns(T::Boolean) }
182
+ def lookup_flag(flag)
183
+ if flag.short
184
+ flags = T.cast(
185
+ parse.select { |node| node.is_a?(Parser::Node::ShortFlag) },
186
+ T::Array[Parser::Node::ShortFlag],
187
+ )
188
+ return true if flags.any? { |node| node.value == flag.short }
189
+ end
190
+ if flag.long
191
+ flags = T.cast(
192
+ parse.select { |node| node.is_a?(Parser::Node::LongFlag) },
193
+ T::Array[Parser::Node::LongFlag],
194
+ )
195
+ return true if flags.any? { |node| node.value == flag.long }
196
+ end
197
+ false
198
+ end
199
+
200
+ sig { params(opt: Definition::Option).returns(T.any(NilClass, String, T::Array[String])) }
201
+ def lookup_option(opt)
202
+ if opt.short
203
+ opts = T.cast(
204
+ parse.select { |node| node.is_a?(Parser::Node::ShortOption) },
205
+ T::Array[Parser::Node::ShortOption],
206
+ )
207
+ matches = opts.select { |node| node.name == opt.short }
208
+ if (last = matches.last)
209
+ return(opt.multi? ? matches.map(&:value) : last.value)
210
+ end
211
+ end
212
+ if opt.long
213
+ opts = T.cast(
214
+ parse.select { |node| node.is_a?(Parser::Node::LongOption) },
215
+ T::Array[Parser::Node::LongOption],
216
+ )
217
+ matches = opts.select { |node| node.name == opt.long }
218
+ if (last = matches.last)
219
+ return(opt.multi? ? matches.map(&:value) : last.value)
220
+ end
221
+ end
222
+ opt.default
223
+ end
224
+
225
+ sig { params(position: Definition::Position).returns(T.any(NilClass, String, T::Array[String])) }
226
+ def lookup_position(position)
227
+ @position_values.fetch(position.name) { position.multi? ? [] : position.default }
228
+ end
229
+
230
+ private
231
+
232
+ sig { returns(T::Array[String]) }
233
+ def args
234
+ @args ||= begin
235
+ nodes = T.cast(
236
+ parse.select { |node| node.is_a?(Parser::Node::Argument) },
237
+ T::Array[Parser::Node::Argument],
238
+ )
239
+ nodes.map(&:value)
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,132 @@
1
+ # typed: true
2
+
3
+ require 'cli/kit'
4
+
5
+ module CLI
6
+ module Kit
7
+ module Args
8
+ class Parser
9
+ class Node
10
+ extend T::Sig
11
+
12
+ sig { void }
13
+ def initialize
14
+ end
15
+
16
+ sig { params(other: T.untyped).returns(T::Boolean) }
17
+ def ==(other)
18
+ self.class == other.class
19
+ end
20
+
21
+ class Option < Node
22
+ extend T::Sig
23
+
24
+ sig { returns(String) }
25
+ attr_reader :name
26
+
27
+ sig { returns(String) }
28
+ attr_reader :value
29
+
30
+ sig { params(name: String, value: String).void }
31
+ def initialize(name, value)
32
+ @name = name
33
+ @value = value
34
+ super()
35
+ end
36
+ private_class_method(:new) # don't instantiate this class directly
37
+
38
+ sig { returns(String) }
39
+ def inspect
40
+ "#<#{self.class.name} #{@name}=#{@value}>"
41
+ end
42
+
43
+ sig { params(other: T.untyped).returns(T::Boolean) }
44
+ def ==(other)
45
+ !!(super(other) && @value == other.value && @name == other.name)
46
+ end
47
+ end
48
+
49
+ class LongOption < Option
50
+ public_class_method(:new)
51
+ end
52
+
53
+ class ShortOption < Option
54
+ public_class_method(:new)
55
+ end
56
+
57
+ class Flag < Node
58
+ sig { returns(String) }
59
+ attr_reader :value
60
+
61
+ sig { params(value: String).void }
62
+ def initialize(value)
63
+ @value = value
64
+ super()
65
+ end
66
+ private_class_method(:new) # don't instantiate this class directly
67
+
68
+ sig { returns(String) }
69
+ def inspect
70
+ "#<#{self.class.name} #{@value}>"
71
+ end
72
+
73
+ sig { params(other: T.untyped).returns(T::Boolean) }
74
+ def ==(other)
75
+ !!(super(other) && @value == other.value)
76
+ end
77
+ end
78
+
79
+ class LongFlag < Flag
80
+ public_class_method(:new)
81
+ end
82
+
83
+ class ShortFlag < Flag
84
+ public_class_method(:new)
85
+ end
86
+
87
+ class Argument < Node
88
+ sig { returns(String) }
89
+ attr_reader :value
90
+
91
+ sig { params(value: String).void }
92
+ def initialize(value)
93
+ @value = value
94
+ super()
95
+ end
96
+
97
+ sig { returns(String) }
98
+ def inspect
99
+ "#<#{self.class.name} #{@value}>"
100
+ end
101
+
102
+ sig { params(other: T.untyped).returns(T::Boolean) }
103
+ def ==(other)
104
+ !!(super(other) && @value == other.value)
105
+ end
106
+ end
107
+
108
+ class Unparsed < Node
109
+ sig { returns(T::Array[String]) }
110
+ attr_reader :value
111
+
112
+ sig { params(value: T::Array[String]).void }
113
+ def initialize(value)
114
+ @value = value
115
+ super()
116
+ end
117
+
118
+ sig { returns(String) }
119
+ def inspect
120
+ "#<#{self.class.name} #{@value.join(" ")}>"
121
+ end
122
+
123
+ sig { params(other: T.untyped).returns(T::Boolean) }
124
+ def ==(other)
125
+ !!(super(other) && @value == other.value)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end