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