1 | /* |
2 | * Copyright 2015-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | #pragma once |
17 | |
18 | #include <functional> |
19 | #include <set> |
20 | #include <stdexcept> |
21 | |
22 | #include <folly/CPortability.h> |
23 | #include <folly/String.h> |
24 | #include <folly/experimental/ProgramOptions.h> |
25 | |
26 | namespace folly { |
27 | |
28 | /** |
29 | * Exception that commands may throw to force the program to exit cleanly |
30 | * with a given exit code. NestedCommandLineApp::run() catches this and |
31 | * makes run() print the given message on stderr (followed by a newline, unless |
32 | * empty; the message is only allowed when exiting with a non-zero status), and |
33 | * return the exit code. (Other exceptions will propagate out of run()) |
34 | */ |
35 | class FOLLY_EXPORT ProgramExit : public std::runtime_error { |
36 | public: |
37 | explicit ProgramExit(int status, const std::string& msg = std::string()); |
38 | int status() const { |
39 | return status_; |
40 | } |
41 | |
42 | private: |
43 | int status_; |
44 | }; |
45 | |
46 | /** |
47 | * App that uses a nested command line, of the form: |
48 | * |
49 | * program [--global_options...] command [--command_options...] command_args... |
50 | */ |
51 | class NestedCommandLineApp { |
52 | public: |
53 | typedef std::function<void( |
54 | const std::string& command, |
55 | const boost::program_options::variables_map& options, |
56 | const std::vector<std::string>& args)> |
57 | InitFunction; |
58 | |
59 | typedef std::function<void( |
60 | const boost::program_options::variables_map& options, |
61 | const std::vector<std::string>&)> |
62 | Command; |
63 | |
64 | static constexpr StringPiece const kHelpCommand = "help" ; |
65 | static constexpr StringPiece const kVersionCommand = "version" ; |
66 | /** |
67 | * Initialize the app. |
68 | * |
69 | * If programName is not set, we try to guess (readlink("/proc/self/exe")). |
70 | * |
71 | * version is the version string printed when given the --version flag. |
72 | * |
73 | * initFunction, if specified, is called after parsing the command line, |
74 | * right before executing the command. |
75 | */ |
76 | explicit NestedCommandLineApp( |
77 | std::string programName = std::string(), |
78 | std::string version = std::string(), |
79 | std::string programHeading = std::string(), |
80 | std::string = std::string(), |
81 | InitFunction initFunction = InitFunction()); |
82 | |
83 | /** |
84 | * Add GFlags to the list of supported options with the given style. |
85 | */ |
86 | void addGFlags(ProgramOptionsStyle style = ProgramOptionsStyle::GNU) { |
87 | globalOptions_.add(getGFlags(style)); |
88 | } |
89 | |
90 | /** |
91 | * Return the global options object, so you can add options. |
92 | */ |
93 | boost::program_options::options_description& globalOptions() { |
94 | return globalOptions_; |
95 | } |
96 | |
97 | /** |
98 | * Add a command. |
99 | * |
100 | * name: command name |
101 | * argStr: description of arguments in help strings |
102 | * (<filename> <N>) |
103 | * shortHelp: one-line summary help string |
104 | * fullHelp: full help string |
105 | * command: function to run |
106 | * |
107 | * Returns a reference to the options_description object that you can |
108 | * use to add options for this command. |
109 | */ |
110 | boost::program_options::options_description& addCommand( |
111 | std::string name, |
112 | std::string argStr, |
113 | std::string shortHelp, |
114 | std::string fullHelp, |
115 | Command command); |
116 | |
117 | /** |
118 | * Add an alias; running the command newName will have the same effect |
119 | * as running oldName. |
120 | */ |
121 | void addAlias(std::string newName, std::string oldName); |
122 | |
123 | /** |
124 | * Run the command and return; the return code is 0 on success or |
125 | * non-zero on error, so it is idiomatic to call this at the end of main(): |
126 | * return app.run(argc, argv); |
127 | * |
128 | * On successful exit, run() will check for errors on stdout (and flush |
129 | * it) to help command-line applications that need to write to stdout |
130 | * (failing to write to stdout is an error). If there is an error on stdout, |
131 | * we'll print a helpful message on stderr and return an error status (1). |
132 | */ |
133 | int run(int argc, const char* const argv[]); |
134 | int run(const std::vector<std::string>& args); |
135 | |
136 | /** |
137 | * Return true if name represent known built-in command (help, version) |
138 | */ |
139 | bool isBuiltinCommand(const std::string& name) const; |
140 | |
141 | private: |
142 | void doRun(const std::vector<std::string>& args); |
143 | const std::string& resolveAlias(const std::string& name) const; |
144 | |
145 | struct CommandInfo { |
146 | std::string argStr; |
147 | std::string shortHelp; |
148 | std::string fullHelp; |
149 | Command command; |
150 | boost::program_options::options_description options; |
151 | }; |
152 | |
153 | const std::pair<const std::string, CommandInfo>& findCommand( |
154 | const std::string& name) const; |
155 | |
156 | void displayHelp( |
157 | const boost::program_options::variables_map& options, |
158 | const std::vector<std::string>& args) const; |
159 | |
160 | void displayVersion() const; |
161 | |
162 | std::string programName_; |
163 | std::string programHeading_; |
164 | std::string programHelpFooter_; |
165 | std::string version_; |
166 | InitFunction initFunction_; |
167 | boost::program_options::options_description globalOptions_; |
168 | std::map<std::string, CommandInfo> commands_; |
169 | std::map<std::string, std::string> aliases_; |
170 | std::set<folly::StringPiece> builtinCommands_; |
171 | }; |
172 | |
173 | } // namespace folly |
174 | |