toys-core 0.3.2

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,99 @@
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 "toys/middleware/base"
31
+
32
+ module Toys
33
+ module Middleware
34
+ ##
35
+ # A middleware that provides switches for editing the verbosity.
36
+ #
37
+ # This middleware adds `-v`, `--verbose`, `-q`, and `--quiet` switches, if
38
+ # not already defined by the tool. These switches affect the setting of
39
+ # {Toys::Context::VERBOSITY}, and, thus, the logger level.
40
+ #
41
+ class AddVerbositySwitches < Base
42
+ ##
43
+ # Default verbose switches
44
+ # @return [Array<String>]
45
+ #
46
+ DEFAULT_VERBOSE_SWITCHES = ["-v", "--verbose"].freeze
47
+
48
+ ##
49
+ # Default quiet switches
50
+ # @return [Array<String>]
51
+ #
52
+ DEFAULT_QUIET_SWITCHES = ["-q", "--quiet"].freeze
53
+
54
+ ##
55
+ # Create a AddVerbositySwitches middleware.
56
+ #
57
+ # @param [Boolean,Array<String>,Proc] verbose_switches Specify switches
58
+ # to increase verbosity. The value may be any of the following:
59
+ # * An array of switches that increase verbosity.
60
+ # * The `true` value to use {DEFAULT_VERBOSE_SWITCHES}. (Default)
61
+ # * The `false` value to disable verbose switches.
62
+ # * A proc that takes a tool and returns any of the above.
63
+ # @param [Boolean,Array<String>,Proc] quiet_switches Specify switches
64
+ # to decrease verbosity. The value may be any of the following:
65
+ # * An array of switches that decrease verbosity.
66
+ # * The `true` value to use {DEFAULT_QUIET_SWITCHES}. (Default)
67
+ # * The `false` value to disable quiet switches.
68
+ # * A proc that takes a tool and returns any of the above.
69
+ #
70
+ def initialize(verbose_switches: true, quiet_switches: true)
71
+ @verbose_switches = verbose_switches
72
+ @quiet_switches = quiet_switches
73
+ end
74
+
75
+ ##
76
+ # Configure the tool switches.
77
+ #
78
+ def config(tool)
79
+ verbose_switches = Middleware.resolve_switches_spec(@verbose_switches, tool,
80
+ DEFAULT_VERBOSE_SWITCHES)
81
+ unless verbose_switches.empty?
82
+ tool.add_switch(Context::VERBOSITY, *verbose_switches,
83
+ doc: "Increase verbosity",
84
+ handler: ->(_val, cur) { cur + 1 },
85
+ only_unique: true)
86
+ end
87
+ quiet_switches = Middleware.resolve_switches_spec(@quiet_switches, tool,
88
+ DEFAULT_QUIET_SWITCHES)
89
+ unless quiet_switches.empty?
90
+ tool.add_switch(Context::VERBOSITY, *quiet_switches,
91
+ doc: "Decrease verbosity",
92
+ handler: ->(_val, cur) { cur - 1 },
93
+ only_unique: true)
94
+ end
95
+ yield
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,51 @@
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
+ module Middleware
32
+ ##
33
+ # A base middleware with a no-op implementation.
34
+ #
35
+ class Base
36
+ ##
37
+ # The base middleware does not affect tool configuration.
38
+ #
39
+ def config(_tool)
40
+ yield
41
+ end
42
+
43
+ ##
44
+ # The base middleware does not affect tool execution.
45
+ #
46
+ def execute(_context)
47
+ yield
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,67 @@
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 "toys/middleware/base"
31
+ require "toys/utils/usage"
32
+
33
+ module Toys
34
+ module Middleware
35
+ ##
36
+ # This middleware handles the case of a usage error. If a usage error, such
37
+ # as an unrecognized switch or an unfulfilled required argument, is
38
+ # detected, this middleware intercepts execution and displays the error
39
+ # along with the usage string, and terminates execution with an error code.
40
+ #
41
+ class HandleUsageErrors < Base
42
+ ##
43
+ # Create a HandleUsageErrors middleware.
44
+ #
45
+ # @param [Intgeer] exit_code The exit code to return if a usage error
46
+ # occurs. Default is -1.
47
+ #
48
+ def initialize(exit_code: -1)
49
+ @exit_code = exit_code
50
+ end
51
+
52
+ ##
53
+ # Intercept and handle usage errors during execution.
54
+ #
55
+ def execute(context)
56
+ if context[Context::USAGE_ERROR]
57
+ puts(context[Context::USAGE_ERROR])
58
+ puts("")
59
+ puts(Utils::Usage.from_context(context).string(show_path: true))
60
+ context.exit(@exit_code)
61
+ else
62
+ yield
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,131 @@
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 "toys/middleware/base"
31
+
32
+ module Toys
33
+ module Middleware
34
+ ##
35
+ # This middleware sets default description fields for tools that do not
36
+ # have them set otherwise. You can set separate descriptions for tools,
37
+ # groups, and the root.
38
+ #
39
+ class SetDefaultDescriptions < Base
40
+ ##
41
+ # The default description for tools.
42
+ # @return [String]
43
+ #
44
+ DEFAULT_TOOL_DESC = "(No description available)".freeze
45
+
46
+ ##
47
+ # The default description for groups.
48
+ # @return [String]
49
+ #
50
+ DEFAULT_GROUP_DESC = "(A group of commands)".freeze
51
+
52
+ ##
53
+ # The default description for the root tool.
54
+ # @return [String]
55
+ #
56
+ DEFAULT_ROOT_DESC =
57
+ "This command line tool was built using the toys-core gem." \
58
+ " See https://www.rubydoc.info/gems/toys-core for more info." \
59
+ " To replace this message, configure the SetDefaultDescriptions" \
60
+ " middleware.".freeze
61
+
62
+ ##
63
+ # Create a SetDefaultDescriptions middleware given default descriptions.
64
+ #
65
+ # @param [String] default_tool_desc The default short description for
66
+ # tools with an eecutor. Defaults to {DEFAULT_TOOL_DESC}.
67
+ # @param [String,nil] default_tool_long_desc The default long description
68
+ # for tools with an eecutor. If `nil` (the default), falls back to
69
+ # the value of the `default_tool_desc` parameter.
70
+ # @param [String] default_group_desc The default short description for
71
+ # tools with no eecutor. Defaults to {DEFAULT_GROUP_DESC}.
72
+ # @param [String,nil] default_group_long_desc The default long
73
+ # description for tools with no eecutor. If `nil` (the default),
74
+ # falls back to the value of the `default_group_desc` parameter.
75
+ # @param [String] default_root_desc The default long description for the
76
+ # root tool. Defaults to {DEFAULT_ROOT_DESC}.
77
+ #
78
+ def initialize(default_tool_desc: DEFAULT_TOOL_DESC,
79
+ default_tool_long_desc: nil,
80
+ default_group_desc: DEFAULT_GROUP_DESC,
81
+ default_group_long_desc: nil,
82
+ default_root_desc: DEFAULT_ROOT_DESC)
83
+ @default_tool_desc = default_tool_desc
84
+ @default_tool_long_desc = default_tool_long_desc
85
+ @default_group_desc = default_group_desc
86
+ @default_group_long_desc = default_group_long_desc
87
+ @default_root_desc = default_root_desc
88
+ end
89
+
90
+ ##
91
+ # Add default description text to tools.
92
+ #
93
+ def config(tool)
94
+ if tool.root?
95
+ config_root_desc(tool)
96
+ elsif tool.includes_executor?
97
+ config_tool_desc(tool)
98
+ else
99
+ config_group_desc(tool)
100
+ end
101
+ yield
102
+ end
103
+
104
+ private
105
+
106
+ def config_root_desc(tool)
107
+ if @default_root_desc && tool.effective_long_desc.empty?
108
+ tool.long_desc = @default_root_desc
109
+ end
110
+ end
111
+
112
+ def config_tool_desc(tool)
113
+ if @default_tool_long_desc && tool.effective_long_desc.empty?
114
+ tool.long_desc = @default_tool_long_desc
115
+ end
116
+ if @default_tool_desc && tool.effective_desc.empty?
117
+ tool.desc = @default_tool_desc
118
+ end
119
+ end
120
+
121
+ def config_group_desc(tool)
122
+ if @default_group_long_desc && tool.effective_long_desc.empty?
123
+ tool.long_desc = @default_group_long_desc
124
+ end
125
+ if @default_group_desc && tool.effective_desc.empty?
126
+ tool.desc = @default_group_desc
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,170 @@
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 "toys/middleware/base"
31
+ require "toys/utils/usage"
32
+
33
+ module Toys
34
+ module Middleware
35
+ ##
36
+ # A middleware that shows usage documentation.
37
+ #
38
+ # This can be configured to display usage text when a switch (typically
39
+ # `--help`) is provided. It can also be configured to display usage text
40
+ # automatically for tools that do not have an executor.
41
+ #
42
+ # If a tool has no executor, this middleware can also add a
43
+ # `--[no-]recursive` flag, which, when set to `true` (the default), shows
44
+ # all subcommands recursively rather than only immediate subcommands.
45
+ #
46
+ class ShowUsage < Base
47
+ ##
48
+ # Default help switches
49
+ # @return [Array<String>]
50
+ #
51
+ DEFAULT_HELP_SWITCHES = ["-?", "--help"].freeze
52
+
53
+ ##
54
+ # Default recursive switches
55
+ # @return [Array<String>]
56
+ #
57
+ DEFAULT_RECURSIVE_SWITCHES = ["-r", "--[no-]recursive"].freeze
58
+
59
+ ##
60
+ # Default search switches
61
+ # @return [Array<String>]
62
+ #
63
+ DEFAULT_SEARCH_SWITCHES = ["-s WORD", "--search=WORD"].freeze
64
+
65
+ ##
66
+ # Create a ShowUsage middleware.
67
+ #
68
+ # @param [Boolean,Array<String>,Proc] help_switches Specify switches to
69
+ # activate help. The value may be any of the following:
70
+ # * An array of switches.
71
+ # * The `true` value to use {DEFAULT_HELP_SWITCHES}. (Default)
72
+ # * The `false` value for no switches.
73
+ # * A proc that takes a tool and returns any of the above.
74
+ # @param [Boolean,Array<String>,Proc] recursive_switches Specify switches
75
+ # to control recursive subcommand search. The value may be any of the
76
+ # following:
77
+ # * An array of switches.
78
+ # * The `true` value to use {DEFAULT_RECURSIVE_SWITCHES}. (Default)
79
+ # * The `false` value for no switches.
80
+ # * A proc that takes a tool and returns any of the above.
81
+ # @param [Boolean,Array<String>,Proc] search_switches Specify switches
82
+ # to search subcommands for a search term. The value may be any of
83
+ # the following:
84
+ # * An array of switches.
85
+ # * The `true` value to use {DEFAULT_SEARCH_SWITCHES}. (Default)
86
+ # * The `false` value for no switches.
87
+ # * A proc that takes a tool and returns any of the above.
88
+ # @param [Boolean] default_recursive Whether to search recursively for
89
+ # subcommands by default. Default is `true`.
90
+ # @param [Boolean] fallback_execution Cause the tool to display its own
91
+ # usage text if it does not otherwise have an executor. This is
92
+ # mostly useful for groups, which have children but no executor.
93
+ # Default is `true`.
94
+ #
95
+ def initialize(help_switches: true,
96
+ recursive_switches: true,
97
+ search_switches: true,
98
+ default_recursive: true,
99
+ fallback_execution: true)
100
+ @help_switches = help_switches
101
+ @recursive_switches = recursive_switches
102
+ @search_switches = search_switches
103
+ @default_recursive = default_recursive ? true : false
104
+ @fallback_execution = fallback_execution
105
+ end
106
+
107
+ ##
108
+ # Configure switches and default data.
109
+ #
110
+ def config(tool)
111
+ help_switches = Middleware.resolve_switches_spec(@help_switches, tool,
112
+ DEFAULT_HELP_SWITCHES)
113
+ is_default = !tool.includes_executor? && @fallback_execution
114
+ if !help_switches.empty?
115
+ doc = "Show help message"
116
+ doc << " (default for groups)" if is_default
117
+ tool.add_switch(:_help, *help_switches,
118
+ doc: doc,
119
+ default: is_default,
120
+ only_unique: true)
121
+ elsif is_default
122
+ tool.default_data[:_help] = true
123
+ end
124
+ if !tool.includes_executor? && (!help_switches.empty? || @fallback_execution)
125
+ add_recursive_switches(tool)
126
+ add_search_switches(tool)
127
+ end
128
+ yield
129
+ end
130
+
131
+ ##
132
+ # Display usage text if requested.
133
+ #
134
+ def execute(context)
135
+ if context[:_help]
136
+ usage = Utils::Usage.from_context(context)
137
+ puts(usage.string(recursive: context[:_recursive_subcommands],
138
+ search: context[:_search_subcommands],
139
+ show_path: context.verbosity > 0))
140
+ else
141
+ yield
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ def add_recursive_switches(tool)
148
+ recursive_switches = Middleware.resolve_switches_spec(@recursive_switches, tool,
149
+ DEFAULT_RECURSIVE_SWITCHES)
150
+ unless recursive_switches.empty?
151
+ tool.add_switch(:_recursive_subcommands, *recursive_switches,
152
+ default: @default_recursive,
153
+ doc: "Show all subcommands recursively" \
154
+ " (default is #{@default_recursive})",
155
+ only_unique: true)
156
+ end
157
+ end
158
+
159
+ def add_search_switches(tool)
160
+ search_switches = Middleware.resolve_switches_spec(@search_switches, tool,
161
+ DEFAULT_SEARCH_SWITCHES)
162
+ unless search_switches.empty?
163
+ tool.add_switch(:_search_subcommands, *search_switches,
164
+ doc: "Search subcommands for the given term",
165
+ only_unique: true)
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end