cli-kit 4.0.0 → 5.0.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +3 -0
  3. data/.github/workflows/cla.yml +22 -0
  4. data/.github/workflows/ruby.yml +16 -2
  5. data/.gitignore +2 -0
  6. data/.rubocop.sorbet.yml +47 -0
  7. data/.rubocop.yml +32 -1
  8. data/.ruby-version +1 -0
  9. data/Gemfile +10 -1
  10. data/Gemfile.lock +102 -29
  11. data/README.md +46 -3
  12. data/Rakefile +1 -0
  13. data/bin/onchange +30 -0
  14. data/bin/tapioca +28 -0
  15. data/bin/testunit +1 -0
  16. data/cli-kit.gemspec +9 -4
  17. data/dev.yml +38 -3
  18. data/examples/minimal/example.rb +11 -6
  19. data/examples/single-file/example.rb +25 -35
  20. data/gen/lib/gen/commands/help.rb +8 -10
  21. data/gen/lib/gen/commands/new.rb +23 -9
  22. data/gen/lib/gen/commands.rb +21 -9
  23. data/gen/lib/gen/entry_point.rb +12 -3
  24. data/gen/lib/gen/generator.rb +32 -11
  25. data/gen/lib/gen/help.rb +63 -0
  26. data/gen/lib/gen.rb +18 -23
  27. data/gen/template/bin/update-deps +2 -2
  28. data/gen/template/dev-gems.yml +1 -1
  29. data/gen/template/dev-vendor.yml +1 -1
  30. data/gen/template/lib/__app__/commands.rb +1 -4
  31. data/gen/template/lib/__app__.rb +8 -17
  32. data/gen/template/test/example_test.rb +1 -1
  33. data/lib/cli/kit/args/definition.rb +344 -0
  34. data/lib/cli/kit/args/evaluation.rb +234 -0
  35. data/lib/cli/kit/args/parser/node.rb +132 -0
  36. data/lib/cli/kit/args/parser.rb +129 -0
  37. data/lib/cli/kit/args/tokenizer.rb +133 -0
  38. data/lib/cli/kit/args.rb +16 -0
  39. data/lib/cli/kit/base_command.rb +17 -32
  40. data/lib/cli/kit/command_help.rb +271 -0
  41. data/lib/cli/kit/command_registry.rb +72 -20
  42. data/lib/cli/kit/config.rb +25 -22
  43. data/lib/cli/kit/core_ext.rb +30 -0
  44. data/lib/cli/kit/error_handler.rb +131 -70
  45. data/lib/cli/kit/executor.rb +20 -3
  46. data/lib/cli/kit/ini.rb +31 -38
  47. data/lib/cli/kit/levenshtein.rb +12 -4
  48. data/lib/cli/kit/logger.rb +16 -2
  49. data/lib/cli/kit/opts.rb +301 -0
  50. data/lib/cli/kit/parse_args.rb +55 -0
  51. data/lib/cli/kit/resolver.rb +8 -0
  52. data/lib/cli/kit/sorbet_runtime_stub.rb +154 -0
  53. data/lib/cli/kit/support/test_helper.rb +27 -16
  54. data/lib/cli/kit/support.rb +2 -0
  55. data/lib/cli/kit/system.rb +194 -57
  56. data/lib/cli/kit/util.rb +48 -103
  57. data/lib/cli/kit/version.rb +3 -1
  58. data/lib/cli/kit.rb +104 -7
  59. metadata +30 -14
  60. data/.github/probots.yml +0 -2
  61. data/lib/cli/kit/autocall.rb +0 -21
  62. 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,234 @@
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.to_sym == 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
+ opts = T.cast(
203
+ parse.select { |node| node.is_a?(Parser::Node::ShortOption) || node.is_a?(Parser::Node::LongOption) },
204
+ T::Array[T.any(Parser::Node::ShortOption, Parser::Node::LongOption)],
205
+ )
206
+ matches = opts.select { |node| (opt.short && node.name == opt.short) || (opt.long && node.name == opt.long) }
207
+ if (last = matches.last)
208
+ return(opt.multi? ? matches.map(&:value) : last.value)
209
+ end
210
+
211
+ opt.default
212
+ end
213
+
214
+ sig { params(position: Definition::Position).returns(T.any(NilClass, String, T::Array[String])) }
215
+ def lookup_position(position)
216
+ @position_values.fetch(position.name) { position.multi? ? [] : position.default }
217
+ end
218
+
219
+ private
220
+
221
+ sig { returns(T::Array[String]) }
222
+ def args
223
+ @args ||= begin
224
+ nodes = T.cast(
225
+ parse.select { |node| node.is_a?(Parser::Node::Argument) },
226
+ T::Array[Parser::Node::Argument],
227
+ )
228
+ nodes.map(&:value)
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ 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