Apache Mesos
subcommand.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_SUBCOMMAND_HPP__
14 #define __STOUT_SUBCOMMAND_HPP__
15 
16 #include <iostream>
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 
21 #include <stout/flags.hpp>
22 #include <stout/foreach.hpp>
23 #include <stout/hashset.hpp>
24 #include <stout/option.hpp>
25 #include <stout/preprocessor.hpp>
26 
27 // Subcommand is an abstraction for creating command binaries that
28 // encompass many subcommands. For example:
29 //
30 // $ ./runner start --arg1=1 --arg2=2
31 // $ ./runner stop --arg3=3 --arg4=4
32 //
33 // Here, the 'runner' command contains two subcommand implementations:
34 // StartCommand and StopCommand. Each subcommand needs to define a
35 // name, implement an 'execute' function, and provide the address of a
36 // flags where the command line arguments will be parsed to. To
37 // simplify creating command binaries that encompass many subcommands,
38 // we provide a 'dispatch' function which will look at argv[1] to
39 // decide which subcommand to execute (based on its name) and then
40 // parse the command line flags for you.
42 {
43 public:
44  // This function is supposed to be called by the main function of
45  // the command binary. A user needs to register at least one
46  // subcommand. Here is a typical example of the main function of the
47  // command binary:
48  //
49  // int main(int argc, char** argv)
50  // {
51  // return Subcommand::dispatch(
52  // None(),
53  // argc,
54  // argv,
55  // new Subcommand1(),
56  // new Subcommand2(),
57  // new Subcommand3());
58  // }
59 #define INSERT(z, N, _) subcommands.push_back( c ## N );
60 #define TEMPLATE(Z, N, DATA) \
61  static int dispatch( \
62  const Option<std::string>& prefix, \
63  int argc, \
64  char** argv, \
65  ENUM_PARAMS(N, Subcommand* c)) \
66  { \
67  std::vector<Subcommand*> subcommands; \
68  REPEAT_FROM_TO(0, N, INSERT, _) \
69  return dispatch(prefix, argc, argv, subcommands); \
70  }
71 
72  REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args C1 -> C11.
73 #undef TEMPLATE
74 #undef INSERT
75 
76  explicit Subcommand(const std::string& _name) : name_(_name) {}
77  virtual ~Subcommand() {}
78 
79  std::string name() const { return name_; }
80 
81 protected:
82  // Defines the main function of this subcommand. The return value
83  // will be used as the exit code.
84  // TODO(jieyu): Consider passing in argc and argv as some users
85  // might want to access the remaining command line arguments.
86  virtual int execute() = 0;
87 
88  // Returns the pointer to the flags that will be used for this
89  // subcommand. If the user does not provide an override, the default
90  // empty flags will be used.
91  virtual flags::FlagsBase* getFlags() { return &flags_; }
92 
93 private:
94  // Returns the usage by listing all the registered subcommands.
95  static std::string usage(
96  const std::string& argv0,
97  const std::vector<Subcommand*>& subcommands);
98 
99  static int dispatch(
101  int argc,
102  char** argv,
103  const std::vector<Subcommand*>& subcommands);
104 
105  // The name of this subcommand.
106  std::string name_;
107 
108  // The default flags which is empty.
109  flags::FlagsBase flags_;
110 };
111 
112 
113 inline std::string Subcommand::usage(
114  const std::string& argv0,
115  const std::vector<Subcommand*>& subcommands)
116 {
117  std::ostringstream stream;
118 
119  stream << "Usage: " << argv0 << " <subcommand> [OPTIONS]\n\n"
120  << "Available subcommands:\n"
121  << " help\n";
122 
123  // Get a list of available subcommands.
124  foreach (Subcommand* subcommand, subcommands) {
125  stream << " " << subcommand->name() << "\n";
126  }
127 
128  return stream.str();
129 }
130 
131 
132 inline int Subcommand::dispatch(
134  int argc,
135  char** argv,
136  const std::vector<Subcommand*>& subcommands)
137 {
138  if (subcommands.empty()) {
139  std::cerr << "No subcommand is found" << std::endl;
140  return 1;
141  }
142 
143  // Check for duplicated subcommand names.
144  hashset<std::string> names;
145  foreach (Subcommand* subcommand, subcommands) {
146  if (names.contains(subcommand->name())) {
147  std::cerr << "Multiple subcommands have name '"
148  << subcommand->name() << "'" << std::endl;
149  return 1;
150  }
151  names.insert(subcommand->name());
152  }
153 
154  if (argc < 2) {
155  std::cerr << usage(argv[0], subcommands) << std::endl;
156  return 1;
157  }
158 
159  if (std::string(argv[1]) == "help") {
160  if (argc == 2) {
161  std::cout << usage(argv[0], subcommands) << std::endl;
162  return 0;
163  }
164 
165  // 'argv[0] help subcommand' => 'argv[0] subcommand --help'
166  argv[1] = argv[2];
167  argv[2] = (char*) "--help";
168  }
169 
170  foreach (Subcommand* subcommand, subcommands) {
171  if (subcommand->name() == argv[1]) {
172  flags::FlagsBase* flags = subcommand->getFlags();
173 
174  Try<flags::Warnings> load = flags->load(prefix, argc - 1, argv + 1);
175  if (load.isError()) {
176  std::cerr << "Failed to parse the flags: " << load.error() << std::endl;
177  return 1;
178  }
179 
180  return subcommand->execute();
181  }
182  }
183 
184  std::cerr << "Subcommand '" << argv[1] << "' is not available\n"
185  << usage(argv[0], subcommands) << std::endl;
186  return 1;
187 }
188 
189 #endif // __STOUT_SUBCOMMAND_HPP__
std::string name() const
Definition: subcommand.hpp:79
Definition: check.hpp:33
virtual int execute()=0
constexpr const char * prefix
Definition: os.hpp:96
bool contains(const Elem &elem) const
Definition: hashset.hpp:102
REPEAT_FROM_TO(1, 11, TEMPLATE, _) explicit Subcommand(const std
Definition: subcommand.hpp:72
virtual flags::FlagsBase * getFlags()
Definition: subcommand.hpp:91
Definition: subcommand.hpp:41
Definition: flags.hpp:44
#define TEMPLATE(Z, N, DATA)
Definition: subcommand.hpp:60
virtual Try< Warnings > load(const std::string &prefix)
Definition: flags.hpp:565
static Try error(const E &e)
Definition: try.hpp:43
bool isError() const
Definition: try.hpp:78
virtual ~Subcommand()
Definition: subcommand.hpp:77
Definition: parse.hpp:33