b4a9865d0e54b98d5141f3ec8083a1164c221604
[folly.git] / folly / experimental / NestedCommandLineApp.h
1 /*
2  * Copyright 2017 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
17 #pragma once
18
19 #include <functional>
20 #include <stdexcept>
21
22 #include <folly/experimental/ProgramOptions.h>
23
24 namespace folly {
25
26 /**
27  * Exception that commands may throw to force the program to exit cleanly
28  * with a given exit code. NestedCommandLineApp::run() catches this and
29  * makes run() print the given message on stderr (followed by a newline, unless
30  * empty; the message is only allowed when exiting with a non-zero status), and
31  * return the exit code. (Other exceptions will propagate out of run())
32  */
33 class ProgramExit : public std::runtime_error {
34  public:
35   explicit ProgramExit(int status, const std::string& msg = std::string());
36   int status() const { return status_; }
37  private:
38   int status_;
39 };
40
41 /**
42  * App that uses a nested command line, of the form:
43  *
44  * program [--global_options...] command [--command_options...] command_args...
45  */
46 class NestedCommandLineApp {
47  public:
48   typedef std::function<void(
49       const std::string& command,
50       const boost::program_options::variables_map& options,
51       const std::vector<std::string>& args)> InitFunction;
52
53   typedef std::function<void(
54       const boost::program_options::variables_map& options,
55       const std::vector<std::string>&)> Command;
56
57   /**
58    * Initialize the app.
59    *
60    * If programName is not set, we try to guess (readlink("/proc/self/exe")).
61    *
62    * version is the version string printed when given the --version flag.
63    *
64    * initFunction, if specified, is called after parsing the command line,
65    * right before executing the command.
66    */
67   explicit NestedCommandLineApp(
68       std::string programName = std::string(),
69       std::string version = std::string(),
70       std::string programHeading = std::string(),
71       std::string programHelpFooter = std::string(),
72       InitFunction initFunction = InitFunction());
73
74   /**
75    * Add GFlags to the list of supported options with the given style.
76    */
77   void addGFlags(ProgramOptionsStyle style = ProgramOptionsStyle::GNU) {
78     globalOptions_.add(getGFlags(style));
79   }
80
81   /**
82    * Return the global options object, so you can add options.
83    */
84   boost::program_options::options_description& globalOptions() {
85     return globalOptions_;
86   }
87
88   /**
89    * Add a command.
90    *
91    * name:  command name
92    * argStr: description of arguments in help strings
93    *   (<filename> <N>)
94    * shortHelp: one-line summary help string
95    * fullHelp: full help string
96    * command: function to run
97    *
98    * Returns a reference to the options_description object that you can
99    * use to add options for this command.
100    */
101   boost::program_options::options_description& addCommand(
102       std::string name,
103       std::string argStr,
104       std::string shortHelp,
105       std::string fullHelp,
106       Command command);
107
108   /**
109    * Add an alias; running the command newName will have the same effect
110    * as running oldName.
111    */
112   void addAlias(std::string newName, std::string oldName);
113
114   /**
115    * Run the command and return; the return code is 0 on success or
116    * non-zero on error, so it is idiomatic to call this at the end of main():
117    * return app.run(argc, argv);
118    *
119    * On successful exit, run() will check for errors on stdout (and flush
120    * it) to help command-line applications that need to write to stdout
121    * (failing to write to stdout is an error). If there is an error on stdout,
122    * we'll print a helpful message on stderr and return an error status (1).
123    */
124   int run(int argc, const char* const argv[]);
125   int run(const std::vector<std::string>& args);
126
127  private:
128   void doRun(const std::vector<std::string>& args);
129   const std::string& resolveAlias(const std::string& name) const;
130
131   struct CommandInfo {
132     std::string argStr;
133     std::string shortHelp;
134     std::string fullHelp;
135     Command command;
136     boost::program_options::options_description options;
137   };
138
139   const std::pair<const std::string, CommandInfo>&
140   findCommand(const std::string& name) const;
141
142   void displayHelp(
143       const boost::program_options::variables_map& options,
144       const std::vector<std::string>& args);
145
146   std::string programName_;
147   std::string programHeading_;
148   std::string programHelpFooter_;
149   std::string version_;
150   InitFunction initFunction_;
151   boost::program_options::options_description globalOptions_;
152   std::map<std::string, CommandInfo> commands_;
153   std::map<std::string, std::string> aliases_;
154 };
155
156 }  // namespaces