tabry 0.1.0
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.
- checksums.yaml +7 -0
- data/bin/tabry-bash +48 -0
- data/bin/tabry-help +17 -0
- data/bin/tabry-test-options +9 -0
- data/bin/tabry-test-parse +20 -0
- data/lib/tabry/cli/arg_proxy.rb +71 -0
- data/lib/tabry/cli/base.rb +34 -0
- data/lib/tabry/cli/builder.rb +104 -0
- data/lib/tabry/cli/flag_proxy.rb +45 -0
- data/lib/tabry/cli/internals.rb +10 -0
- data/lib/tabry/cli/util/config.rb +48 -0
- data/lib/tabry/cli/util.rb +51 -0
- data/lib/tabry/config_loader.rb +55 -0
- data/lib/tabry/core_ext/string/colors.rb +91 -0
- data/lib/tabry/machine.rb +124 -0
- data/lib/tabry/models/arg.rb +43 -0
- data/lib/tabry/models/arg_base.rb +10 -0
- data/lib/tabry/models/arg_include.rb +9 -0
- data/lib/tabry/models/arg_includes.rb +14 -0
- data/lib/tabry/models/args_list.rb +31 -0
- data/lib/tabry/models/config.rb +44 -0
- data/lib/tabry/models/config_error.rb +8 -0
- data/lib/tabry/models/config_list.rb +48 -0
- data/lib/tabry/models/config_object.rb +78 -0
- data/lib/tabry/models/config_string_hash.rb +44 -0
- data/lib/tabry/models/const_option.rb +17 -0
- data/lib/tabry/models/dir_option.rb +25 -0
- data/lib/tabry/models/file_option.rb +25 -0
- data/lib/tabry/models/flag.rb +55 -0
- data/lib/tabry/models/flags_list.rb +47 -0
- data/lib/tabry/models/include_arg.rb +18 -0
- data/lib/tabry/models/include_flag.rb +18 -0
- data/lib/tabry/models/include_option.rb +22 -0
- data/lib/tabry/models/include_sub.rb +18 -0
- data/lib/tabry/models/option.rb +33 -0
- data/lib/tabry/models/option_base.rb +20 -0
- data/lib/tabry/models/option_includes.rb +14 -0
- data/lib/tabry/models/options_list.rb +18 -0
- data/lib/tabry/models/shell_option.rb +13 -0
- data/lib/tabry/models/sub.rb +59 -0
- data/lib/tabry/models/subs_list.rb +28 -0
- data/lib/tabry/options_finder.rb +87 -0
- data/lib/tabry/result.rb +110 -0
- data/lib/tabry/runner.rb +27 -0
- data/lib/tabry/state.rb +14 -0
- data/lib/tabry/usage_generator.rb +137 -0
- data/lib/tabry/util.rb +15 -0
- data/sh/tabry_bash.sh +61 -0
- data/sh/tabry_bash_help.sh +7 -0
- data/spec/fixtures/basiccli.json +1 -0
- data/spec/fixtures/basiccli.tabry +5 -0
- data/spec/fixtures/basiccli2.tabry +5 -0
- data/spec/fixtures/basiccli2.yml +7 -0
- data/spec/fixtures/vehicles.tabry +60 -0
- data/spec/fixtures/vehicles.yaml +135 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/tabry/cli/arg_proxy_spec.rb +76 -0
- data/spec/tabry/cli/builder_spec.rb +226 -0
- data/spec/tabry/config_loader_spec.rb +69 -0
- data/spec/tabry/machine_spec.rb +109 -0
- data/spec/tabry/models/args_list_spec.rb +36 -0
- data/spec/tabry/models/config_spec.rb +62 -0
- data/spec/tabry/models/const_option_spec.rb +17 -0
- data/spec/tabry/models/dir_option_spec.rb +16 -0
- data/spec/tabry/models/file_option_spec.rb +16 -0
- data/spec/tabry/models/options_list_spec.rb +47 -0
- data/spec/tabry/models/shell_option_spec.rb +19 -0
- data/spec/tabry/models/subs_list_spec.rb +24 -0
- data/spec/tabry/options_finder_spec.rb +91 -0
- data/spec/tabry/result_spec.rb +236 -0
- data/spec/tabry/runner_spec.rb +35 -0
- data/spec/tabry/usage_generator_spec.rb +67 -0
- data/tabry.gemspec +27 -0
- metadata +189 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "state"
|
4
|
+
require_relative "util"
|
5
|
+
|
6
|
+
# The core Tabry state machine / parser which reads tokens, figures out if it's
|
7
|
+
# a sub/flag/arg/etc. and advances the state
|
8
|
+
#
|
9
|
+
# The output is a State object -- the final state of the machine.
|
10
|
+
module Tabry
|
11
|
+
class Machine
|
12
|
+
attr_reader :state, :config
|
13
|
+
|
14
|
+
# Returns a state
|
15
|
+
def self.run(config, tokens)
|
16
|
+
machine = new(config)
|
17
|
+
machine.run(tokens)
|
18
|
+
machine.state
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
@config = config
|
23
|
+
@state = State.new(mode: :subcommand, subcommand_stack: [], args: [], flags: {})
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(tokens)
|
27
|
+
tokens.each do |token|
|
28
|
+
step(token)
|
29
|
+
Tabry::Util.debug "AFTER token #{token.inspect}"
|
30
|
+
Tabry::Util.debug "STATE: #{state.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def step(token)
|
37
|
+
send :"step_#{state.mode}", token
|
38
|
+
end
|
39
|
+
|
40
|
+
def current_sub
|
41
|
+
# TODO: a bit of a waste to look this up every time.
|
42
|
+
config.dig_sub(state.subcommand_stack)
|
43
|
+
end
|
44
|
+
|
45
|
+
def step_subcommand(token)
|
46
|
+
step_subcommand_match_subcommand(token) ||
|
47
|
+
step_subcommand_match_dashdash(token) ||
|
48
|
+
step_subcommand_match_flag(token) ||
|
49
|
+
step_subcommand_match_help(token) ||
|
50
|
+
step_subcommand_match_arg(token)
|
51
|
+
end
|
52
|
+
|
53
|
+
def step_subcommand_match_help(token)
|
54
|
+
return false if state.help
|
55
|
+
return false unless state.mode == :subcommand
|
56
|
+
|
57
|
+
if (state.subcommand_stack.empty? && token == "help") ||
|
58
|
+
(!state.dashdash && %w[--help -?].include?(token))
|
59
|
+
state.help = true
|
60
|
+
Tabry::Util.debug "MATCHED help ON token #{token.inspect}"
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def step_subcommand_match_subcommand(token)
|
67
|
+
return false unless state.args.empty?
|
68
|
+
|
69
|
+
sub = current_sub.subs.match(token)
|
70
|
+
return false unless sub
|
71
|
+
|
72
|
+
state.subcommand_stack << sub.name
|
73
|
+
Tabry::Util.debug "MATCHED sub #{sub.name} ON token #{token.inspect}"
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def step_subcommand_match_dashdash(token)
|
78
|
+
return false if state.dashdash
|
79
|
+
return false unless token == "--"
|
80
|
+
|
81
|
+
state.dashdash = true
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def step_subcommand_match_flag(token)
|
86
|
+
return false if state.dashdash
|
87
|
+
|
88
|
+
flag, arg_value = nil
|
89
|
+
# Reverse: most specific sub's flag takes precedence in case of multiple matching
|
90
|
+
config.dig_sub_array(state.subcommand_stack).reverse.each do |sub|
|
91
|
+
flag, arg_value = sub.flags.match(token)
|
92
|
+
break if flag
|
93
|
+
end
|
94
|
+
return false unless flag
|
95
|
+
|
96
|
+
if arg_value
|
97
|
+
state.flags[flag.name] = arg_value
|
98
|
+
elsif flag.arg
|
99
|
+
state.mode = :flagarg
|
100
|
+
state.current_flag = flag.name
|
101
|
+
else
|
102
|
+
state.flags[flag.name] = true
|
103
|
+
end
|
104
|
+
|
105
|
+
Tabry::Util.debug "MATCHED flag #{flag.name} ON token #{token.inspect}"
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
# this accepts anything, for now...
|
110
|
+
def step_subcommand_match_arg(token)
|
111
|
+
state.args << token
|
112
|
+
|
113
|
+
Tabry::Util.debug "MATCHED arg ON token #{token.inspect}"
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
# Accepts anything, for now
|
118
|
+
def step_flagarg(token)
|
119
|
+
state.flags[state.current_flag] = token
|
120
|
+
state.mode = :subcommand
|
121
|
+
true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "arg_base"
|
4
|
+
require_relative "options_list"
|
5
|
+
require_relative "include_arg"
|
6
|
+
|
7
|
+
module Tabry
|
8
|
+
module Models
|
9
|
+
class Arg < ArgBase
|
10
|
+
def self.new(**args)
|
11
|
+
if args[:raw]["include"]
|
12
|
+
IncludeArg.new(**args)
|
13
|
+
else
|
14
|
+
super(**args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FIELDS = {
|
19
|
+
name: :string,
|
20
|
+
options: [:list_object, :OptionsList],
|
21
|
+
optional: :boolean,
|
22
|
+
description: :string,
|
23
|
+
varargs: :boolean,
|
24
|
+
title: :string,
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
attr_reader(*FIELDS.keys)
|
28
|
+
|
29
|
+
def flatten
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alias (more clear intention)
|
34
|
+
def varargs?
|
35
|
+
varargs
|
36
|
+
end
|
37
|
+
|
38
|
+
def title
|
39
|
+
@title || name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_string_hash"
|
4
|
+
require_relative "arg_include"
|
5
|
+
|
6
|
+
module Tabry
|
7
|
+
module Models
|
8
|
+
class ArgIncludes < ConfigStringHash
|
9
|
+
def initialize(**args)
|
10
|
+
super(**args, klass: ArgInclude)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "arg"
|
4
|
+
require_relative "config_error"
|
5
|
+
|
6
|
+
module Tabry
|
7
|
+
module Models
|
8
|
+
class ArgsList < ConfigList
|
9
|
+
def initialize(**args)
|
10
|
+
super(**args, klass: Arg)
|
11
|
+
end
|
12
|
+
|
13
|
+
def n_passed_in_varargs(n_total_passed_in_args)
|
14
|
+
case to_a.count(&:varargs?)
|
15
|
+
when 0
|
16
|
+
0
|
17
|
+
when 1
|
18
|
+
n_regular_passed_in_args = to_a.length - 1
|
19
|
+
[n_total_passed_in_args - n_regular_passed_in_args, 0].max
|
20
|
+
else
|
21
|
+
arg_names = to_a.map { |a| "#{a.name || "[unnamed]"}#{a.varargs? ? " (varargs)" : ""}" }
|
22
|
+
raise ConfigError, "More than one varargs args in one sub: #{arg_names.join(", ")}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def varargs_arg
|
27
|
+
to_a.find(&:varargs?)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_object"
|
4
|
+
require_relative "sub"
|
5
|
+
require_relative "option_includes"
|
6
|
+
require_relative "arg_includes"
|
7
|
+
|
8
|
+
module Tabry
|
9
|
+
module Models
|
10
|
+
class Config < ConfigObject
|
11
|
+
FIELDS = {
|
12
|
+
cmd: :string,
|
13
|
+
main: [:object, :Sub],
|
14
|
+
option_includes: [:object, :OptionIncludes],
|
15
|
+
arg_includes: [:object, :ArgIncludes],
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
attr_reader(*FIELDS.keys)
|
19
|
+
|
20
|
+
def initialize(raw:)
|
21
|
+
super(raw: raw, root: self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def dig_sub(sub_stack)
|
25
|
+
sub_stack.reduce(main) { |sub, sub_name| sub.subs.by_name[sub_name] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def dig_sub_array(sub_stack)
|
29
|
+
subs = [main]
|
30
|
+
sub_stack.each do |sub_name|
|
31
|
+
subs << subs.last.subs.by_name[sub_name]
|
32
|
+
end
|
33
|
+
subs
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
# TODO: remove hack, but make everything in models have a hack so _root is not shown
|
38
|
+
return "#<Tabry::Config>" if caller.any? { |c| c.include?("in `inspect'") }
|
39
|
+
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_error"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class ConfigList
|
8
|
+
attr_reader :unflattened
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
def initialize(raw:, root:, klass:)
|
13
|
+
raise "missing root" unless root
|
14
|
+
|
15
|
+
raw ||= []
|
16
|
+
unless raw.is_a?(Array)
|
17
|
+
raise ConfigError, "#{self.class.name} must be an array. Got #{raw.class}"
|
18
|
+
end
|
19
|
+
|
20
|
+
@unflattened = raw.map { |a| klass.new(raw: a, root: root) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_a
|
24
|
+
flatten
|
25
|
+
end
|
26
|
+
|
27
|
+
def flatten
|
28
|
+
@flatten ||= unflattened.map(&:flatten).flatten
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](*args)
|
32
|
+
to_a.[](*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(...)
|
36
|
+
to_a.each(...)
|
37
|
+
end
|
38
|
+
|
39
|
+
def length
|
40
|
+
to_a.length
|
41
|
+
end
|
42
|
+
|
43
|
+
def empty?
|
44
|
+
to_a.empty?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_error"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class ConfigObject
|
8
|
+
def to_s
|
9
|
+
inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
field_strings = self.class::FIELDS.keys.map do |f|
|
14
|
+
val = instance_variable_get(:"@#{f}")
|
15
|
+
if val.nil?
|
16
|
+
nil
|
17
|
+
else
|
18
|
+
"#{f}=#{val.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
desc = [self.class.name, *field_strings.compact].join(" ")
|
22
|
+
"#<#{desc}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :_root, :_raw
|
26
|
+
|
27
|
+
def initialize(raw:, root:)
|
28
|
+
@_raw = raw
|
29
|
+
@_root = root or raise "missing root"
|
30
|
+
unknown_fields = @_raw.keys - self.class::FIELDS.keys.map(&:to_s)
|
31
|
+
unless unknown_fields.empty?
|
32
|
+
raise ConfigError, "Unknown field(s) #{unknown_fields.inspect} for #{self.class}"
|
33
|
+
end
|
34
|
+
|
35
|
+
raw.each do |key, val|
|
36
|
+
type, *extra = Array(self.class::FIELDS[key.to_sym])
|
37
|
+
instance_variable_set :"@#{key}", send(:"init_field_#{type}", key, val, *extra)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets fields, asserts is either nil or of given type
|
42
|
+
def assert_of_class(key, val, klasses)
|
43
|
+
unless Array(klasses).any? { |klass| val.is_a?(klass) }
|
44
|
+
raise ConfigError,
|
45
|
+
"Invalid type #{val.class} for #{self.class} field #{key.inspect}, expected #{klasses.inspect}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def init_field_string(key, val)
|
50
|
+
assert_of_class(key, val, String)
|
51
|
+
val
|
52
|
+
end
|
53
|
+
|
54
|
+
def init_field_string_array(key, vals)
|
55
|
+
assert_of_class(key, vals, Array)
|
56
|
+
vals.each_with_index do |val, i|
|
57
|
+
assert_of_class("#{key}[#{i}]", val, String)
|
58
|
+
end
|
59
|
+
vals
|
60
|
+
end
|
61
|
+
|
62
|
+
def init_field_object(key, val, object_class)
|
63
|
+
assert_of_class(key, val, Hash)
|
64
|
+
Object.const_get("Tabry::Models::#{object_class}").new(raw: val, root: _root)
|
65
|
+
end
|
66
|
+
|
67
|
+
def init_field_list_object(key, val, object_class)
|
68
|
+
assert_of_class(key, val, Array)
|
69
|
+
Object.const_get("Tabry::Models::#{object_class}").new(raw: val, root: _root)
|
70
|
+
end
|
71
|
+
|
72
|
+
def init_field_boolean(key, val)
|
73
|
+
assert_of_class(key, val, [TrueClass, FalseClass])
|
74
|
+
val
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_error"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class ConfigStringHash
|
8
|
+
attr_reader :to_h, :_raw, :_root
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
def initialize(raw:, root:, klass:)
|
13
|
+
@_raw = raw
|
14
|
+
@_root = root
|
15
|
+
|
16
|
+
raw ||= {}
|
17
|
+
unless raw.is_a?(Hash)
|
18
|
+
raise ConfigError, "#{self.class.name} must be a Hash. Got #{raw.class}"
|
19
|
+
end
|
20
|
+
unless raw.keys.all? { |k| k.is_a?(String) }
|
21
|
+
raise ConfigError, "#{self.class.name} keys must all be string keys."
|
22
|
+
end
|
23
|
+
|
24
|
+
@to_h = raw.transform_values { |a| klass.new(raw: a, root: root) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](*args)
|
28
|
+
to_h.[](*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(...)
|
32
|
+
to_h.each(...)
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty?
|
36
|
+
to_h.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def keys
|
40
|
+
to_h.keys
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_object"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class DirOption < ConfigObject
|
8
|
+
FIELDS = {
|
9
|
+
type: :string,
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_reader(*FIELDS.keys)
|
13
|
+
|
14
|
+
# Handled by tabru=bash/tabry-bash.sh/shell, we just return a symbol to
|
15
|
+
# communicate to tabry-bash
|
16
|
+
def options(_token)
|
17
|
+
[:directory]
|
18
|
+
end
|
19
|
+
|
20
|
+
def flatten
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_object"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class FileOption < ConfigObject
|
8
|
+
FIELDS = {
|
9
|
+
type: :string,
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_reader(*FIELDS.keys)
|
13
|
+
|
14
|
+
# Handled by tabru=bash/tabry-bash.sh/shell, we just return a symbol to
|
15
|
+
# communicate to tabry-bash
|
16
|
+
def options(_token)
|
17
|
+
[:file]
|
18
|
+
end
|
19
|
+
|
20
|
+
def flatten
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_object"
|
4
|
+
require_relative "options_list"
|
5
|
+
require_relative "include_flag"
|
6
|
+
|
7
|
+
module Tabry
|
8
|
+
module Models
|
9
|
+
class Flag < ConfigObject
|
10
|
+
def self.new(**args)
|
11
|
+
if args[:raw]["include"]
|
12
|
+
IncludeFlag.new(**args)
|
13
|
+
else
|
14
|
+
super(**args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FIELDS = {
|
19
|
+
aliases: :string_array,
|
20
|
+
description: :string,
|
21
|
+
name: :string, # TODO: required
|
22
|
+
required: :boolean,
|
23
|
+
arg: :boolean,
|
24
|
+
options: [:list_object, :OptionsList]
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def flatten
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def match_with_value(token)
|
32
|
+
[name, *aliases].each do |al|
|
33
|
+
if al.length > 1 && token.start_with?("--#{al}=")
|
34
|
+
return token.sub("--#{al}=", "")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def match(token)
|
41
|
+
[name, *aliases].any? { |al| token == alias_with_dash(al) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def name_with_dashes
|
45
|
+
@name_with_dashes ||= alias_with_dash(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def alias_with_dash(ali)
|
49
|
+
(ali.length == 1) ? "-#{ali}" : "--#{ali}"
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader(*FIELDS.keys)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "config_list"
|
4
|
+
require_relative "flag"
|
5
|
+
|
6
|
+
module Tabry
|
7
|
+
module Models
|
8
|
+
class FlagsList < ConfigList
|
9
|
+
def initialize(**args)
|
10
|
+
super(**args, klass: Flag)
|
11
|
+
end
|
12
|
+
|
13
|
+
def first_required_flag(used:)
|
14
|
+
to_a.find do |flag|
|
15
|
+
flag.required && !used[flag.name]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def options(token, used:)
|
20
|
+
to_a.map do |flag|
|
21
|
+
if token&.start_with?("-") && flag.name_with_dashes.start_with?(token) && !used[flag.name]
|
22
|
+
flag.name_with_dashes
|
23
|
+
end
|
24
|
+
end.compact
|
25
|
+
end
|
26
|
+
|
27
|
+
def match(token)
|
28
|
+
to_a.each do |flag|
|
29
|
+
if (arg_value = flag.match_with_value(token))
|
30
|
+
return flag, arg_value
|
31
|
+
elsif flag.match(token)
|
32
|
+
return flag
|
33
|
+
end
|
34
|
+
end
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](flag_name)
|
39
|
+
if flag_name.is_a?(Integer)
|
40
|
+
to_a[flag_name]
|
41
|
+
else
|
42
|
+
to_a.find { |f| f.name == flag_name }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tabry
|
4
|
+
module Models
|
5
|
+
class IncludeArg
|
6
|
+
attr_reader :include_name, :_root
|
7
|
+
|
8
|
+
def initialize(raw:, root:)
|
9
|
+
@include_name = raw["include"]
|
10
|
+
@_root = root
|
11
|
+
end
|
12
|
+
|
13
|
+
def flatten
|
14
|
+
_root.arg_includes[include_name].args.flatten
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tabry
|
4
|
+
module Models
|
5
|
+
class IncludeFlag
|
6
|
+
attr_reader :include_name, :_root
|
7
|
+
|
8
|
+
def initialize(raw:, root:)
|
9
|
+
@include_name = raw["include"]
|
10
|
+
@_root = root
|
11
|
+
end
|
12
|
+
|
13
|
+
def flatten
|
14
|
+
_root.arg_includes[include_name].flags.flatten
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "option_base"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class IncludeOption < OptionBase
|
8
|
+
attr_reader :include_name, :_root
|
9
|
+
|
10
|
+
def options(token)
|
11
|
+
flatten.options(token)
|
12
|
+
end
|
13
|
+
|
14
|
+
def flatten
|
15
|
+
inc = _root.option_includes[value]
|
16
|
+
raise "Bad tabry config: include #{value.inspect} not found" unless inc
|
17
|
+
|
18
|
+
inc.flatten
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|