Apache Mesos
flags.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_FLAGS_FLAGS_HPP__
14 #define __STOUT_FLAGS_FLAGS_HPP__
15 
16 #include <algorithm>
17 #include <map>
18 #include <ostream>
19 #include <string>
20 #include <typeinfo> // For typeid.
21 #include <vector>
22 
23 #include <stout/error.hpp>
24 #include <stout/exit.hpp>
25 #include <stout/foreach.hpp>
26 #include <stout/lambda.hpp>
27 #include <stout/multimap.hpp>
28 #include <stout/none.hpp>
29 #include <stout/nothing.hpp>
30 #include <stout/option.hpp>
31 #include <stout/path.hpp>
32 #include <stout/some.hpp>
33 #include <stout/stringify.hpp>
34 #include <stout/strings.hpp>
35 #include <stout/try.hpp>
36 
37 #include <stout/flags/fetch.hpp>
38 #include <stout/flags/flag.hpp>
39 
40 #include <stout/os/environment.hpp>
41 
42 namespace flags {
43 
44 class FlagsBase
45 {
46 public:
48  {
49  add(&FlagsBase::help, "help", "Prints this help message", false);
50  }
51 
52  virtual ~FlagsBase() = default;
53 
54  // Explicitly disable rvalue constructors and assignment operators
55  // since we plan for this class to be used in virtual inheritance
56  // scenarios. Here e.g., constructing from an rvalue will be
57  // problematic since we can potentially have multiple lineages
58  // leading to the same base class, and could then potentially use a
59  // moved from base object.
60  // All of the following functions would be implicitly generated for
61  // C++14, but in C++11 only the versions taking lvalue references
62  // should be. GCC seems to create all of these even in C++11 mode so
63  // we need to explicitly disable them.
64  FlagsBase(const FlagsBase&) = default;
65  FlagsBase(FlagsBase&&) = delete;
66  FlagsBase& operator=(const FlagsBase&) = default;
67  FlagsBase& operator=(FlagsBase&&) = delete;
68 
69  // Load any flags from the environment given the variable prefix,
70  // i.e., given prefix 'STOUT_' will load a flag named 'foo' via
71  // environment variables 'STOUT_foo' or 'STOUT_FOO'.
72  virtual Try<Warnings> load(const std::string& prefix);
73 
74  // Load any flags from the environment given the variable prefix
75  // (see above) followed by loading from the command line (via 'argc'
76  // and 'argv'). If 'unknowns' is true then we'll ignore unknown
77  // flags we see while loading. If 'duplicates' is true then we'll
78  // ignore any duplicates we see while loading. Note that if a flag
79  // exists in the environment and the command line, the latter takes
80  // precedence.
81  virtual Try<Warnings> load(
82  const Option<std::string>& prefix,
83  int argc,
84  const char* const* argv,
85  bool unknowns = false,
86  bool duplicates = false);
87 
88  // Loads any flags from the environment as above but remove processed
89  // flags from 'argv' and update 'argc' appropriately. For example:
90  //
91  // argv = ["/path/program", "--arg1", "hi", "--arg2", "--", "bye"]
92  //
93  // Becomes:
94  //
95  // argv = ["/path/program", "hi", "bye"]
96  virtual Try<Warnings> load(
97  const Option<std::string>& prefix,
98  int* argc,
99  char*** argv,
100  bool unknowns = false,
101  bool duplicates = false);
102 
103  // Loads flags from the given map where the keys represent the flag
104  // name and the values represent the flag values. For example:
105  //
106  // values = { 'arg1': 'hi',
107  // 'arg2': 'bye' }
108  //
109  // Optionally, if `prefix` is specified, flags will also be loaded
110  // from environment variables with the given prefix.
111  // Note that if a flag exists in both the environment and the values map,
112  // the latter takes precedence.
113  virtual Try<Warnings> load(
114  const std::map<std::string, Option<std::string>>& values,
115  bool unknowns = false,
116  const Option<std::string>& prefix = None());
117 
118  // Loads flags from the map and optionally the environment vars
119  // if a prefix is specified. Follows the behavior of the
120  // method `load(values, unknowns, prefix)` above in terms
121  // of precedence and load order.
122  virtual Try<Warnings> load(
123  const std::map<std::string, std::string>& values,
124  bool unknowns = false,
125  const Option<std::string>& prefix = None());
126 
127  // Returns a string describing the flags, preceded by a "usage
128  // message" that will be prepended to that description (see
129  // 'FlagsBase::usageMessage_').
130  //
131  // The optional 'message' passed to this function will be prepended
132  // to the generated string returned from this function.
133  //
134  // Derived classes and clients can modify the standard usage message
135  // by setting it before calling this method via 'setUsageMessage()'.
136  //
137  // This allows one to set a generic message that will be used for
138  // each invocation of this method, and to additionally add one for
139  // the particular invocation:
140  //
141  // MyFlags flags;
142  // flags.setUsageMessage("Custom Usage Message");
143  // ...
144  // if (flags.foo.isNone()) {
145  // std::cerr << flags.usage("Missing required --foo flag");
146  // }
147  //
148  // The 'message' would be emitted first, followed by a blank line,
149  // then the 'usageMessage_', finally followed by the flags'
150  // description, for example:
151  //
152  // Missing required --foo flag
153  //
154  // Custom Usage Message
155  //
156  // --[no-]help Prints this help message. (default: false)
157  // --foo=VALUE Description about 'foo' here.
158  // --bar=VALUE Description about 'bar' here. (default: 42)
159  //
160  std::string usage(const Option<std::string>& message = None()) const;
161 
162  // Sets the default message that is prepended to the flags'
163  // description in 'usage()'.
164  void setUsageMessage(const std::string& message)
165  {
166  usageMessage_ = Some(message);
167  }
168 
169  typedef std::map<std::string, Flag>::const_iterator const_iterator;
170 
171  const_iterator begin() const { return flags_.begin(); }
172  const_iterator end() const { return flags_.end(); }
173 
174  typedef std::map<std::string, Flag>::iterator iterator;
175 
176  iterator begin() { return flags_.begin(); }
177  iterator end() { return flags_.end(); }
178 
179  // In the overloaded function signatures for `add` found below, we use a `T2*`
180  // for the default flag value where applicable. This is used instead of an
181  // `Option<T2>&` because when string literals are passed to this parameter,
182  // `Option` infers a character array type, which causes problems in the
183  // current implementation of `Option`. See MESOS-5471.
184 
185  template <typename Flags, typename T1, typename T2, typename F>
186  void add(
187  T1 Flags::*t1,
188  const Name& name,
189  const Option<Name>& alias,
190  const std::string& help,
191  const T2* t2,
192  F validate);
193 
194  template <typename Flags, typename T1, typename T2, typename F>
195  void add(
196  T1 Flags::*t1,
197  const Name& name,
198  const Option<Name>& alias,
199  const std::string& help,
200  const T2& t2,
201  F validate)
202  {
203  add(t1, name, alias, help, &t2, validate);
204  }
205 
206  template <typename Flags, typename T1, typename T2, typename F>
207  void add(
208  T1 Flags::*t1,
209  const Name& name,
210  const std::string& help,
211  const T2& t2,
212  F validate)
213  {
214  add(t1, name, None(), help, &t2, validate);
215  }
216 
217  template <typename Flags, typename T1, typename T2>
218  void add(
219  T1 Flags::*t1,
220  const Name& name,
221  const std::string& help,
222  const T2& t2)
223  {
224  add(t1, name, None(), help, &t2, [](const T1&) { return None(); });
225  }
226 
227  template <typename Flags, typename T>
228  void add(
229  T Flags::*t,
230  const Name& name,
231  const std::string& help)
232  {
233  add(t,
234  name,
235  None(),
236  help,
237  static_cast<const T*>(nullptr),
238  [](const T&) { return None(); });
239  }
240 
241  template <typename Flags, typename T1, typename T2>
242  void add(
243  T1 Flags::*t1,
244  const Name& name,
245  const Option<Name>& alias,
246  const std::string& help,
247  const T2& t2)
248  {
249  add(t1, name, alias, help, &t2, [](const T1&) { return None(); });
250  }
251 
252  template <typename Flags, typename T, typename F>
253  void add(
254  Option<T> Flags::*option,
255  const Name& name,
256  const Option<Name>& alias,
257  const std::string& help,
258  F validate);
259 
260  template <typename Flags, typename T, typename F>
261  void add(
262  Option<T> Flags::*option,
263  const Name& name,
264  const std::string& help,
265  F validate)
266  {
267  add(option, name, None(), help, validate);
268  }
269 
270  template <typename Flags, typename T>
271  void add(
272  Option<T> Flags::*option,
273  const Name& name,
274  const std::string& help)
275  {
276  add(option, name, None(), help, [](const Option<T>&) { return None(); });
277  }
278 
279  template <typename Flags, typename T>
280  void add(
281  Option<T> Flags::*option,
282  const Name& name,
283  const Option<Name>& alias,
284  const std::string& help)
285  {
286  add(option, name, alias, help, [](const Option<T>&) { return None(); });
287  }
288 
289  void add(const Flag& flag);
290 
291  // TODO(marco): IMO the entire --help functionality should be
292  // encapsulated inside the FlagsBase class.
293  // For now, exposing this for the caller(s) to decide what to
294  // do when the user asks for help.
295  bool help;
296 
297  // Extract environment variable "flags" with the specified prefix.
298  std::map<std::string, Option<std::string>> extract(
299  const std::string& prefix) const;
300 
301  // Build environment variables from the flags.
302  std::map<std::string, std::string> buildEnvironment(
303  const Option<std::string>& prefix = None()) const;
304 
305 protected:
306  // The program's name, extracted from argv[0] by default;
307  // declared 'protected' so that derived classes can alter this
308  // behavior.
309  std::string programName_;
310 
311  // An optional custom usage message, will be printed 'as is'
312  // just above the list of flags and their explanation.
313  // It is 'None' by default, in which case the default
314  // behavior is to print "Usage:" followed by the 'programName_'.
316 
317 private:
319  Multimap<std::string, Option<std::string>>& values,
320  bool unknowns = false,
321  bool duplicates = false,
322  const Option<std::string>& prefix = None());
323 
324  // Maps flag's name to flag.
325  std::map<std::string, Flag> flags_;
326 
327  // Maps flag's alias to flag's name.
328  std::map<std::string, std::string> aliases;
329 };
330 
331 
332 template <typename Flags, typename T1, typename T2, typename F>
334  T1 Flags::*t1,
335  const Name& name,
336  const Option<Name>& alias,
337  const std::string& help,
338  const T2* t2,
339  F validate)
340 {
341  // Don't bother adding anything if the pointer is `nullptr`.
342  if (t1 == nullptr) {
343  return;
344  }
345 
346  Flags* flags = dynamic_cast<Flags*>(this);
347  if (flags == nullptr) {
348  ABORT("Attempted to add flag '" + name.value +
349  "' with incompatible type");
350  }
351 
352  Flag flag;
353  flag.name = name;
354  flag.alias = alias;
355  flag.help = help;
356  flag.boolean = typeid(T1) == typeid(bool);
357 
358  if (t2 != nullptr) {
359  flags->*t1 = *t2; // Set the default.
360  flag.required = false;
361  } else {
362  flag.required = true;
363  }
364 
365  // NOTE: We need to take FlagsBase* (or const FlagsBase&) as the
366  // first argument to 'load', 'stringify', and 'validate' so that we
367  // use the correct instance of FlagsBase. In other words, we can't
368  // capture 'this' here because it's possible that the FlagsBase
369  // object that we're working with when we invoke FlagsBase::add is
370  // not the same instance as 'this' when these lambdas get invoked.
371 
372  flag.load = [t1](FlagsBase* base, const std::string& value) -> Try<Nothing> {
373  Flags* flags = dynamic_cast<Flags*>(base);
374  if (flags != nullptr) {
375  // NOTE: 'fetch' "retrieves" the value if necessary and then
376  // invokes 'parse'. See 'fetch' for more details.
377  Try<T1> t = fetch<T1>(value);
378  if (t.isSome()) {
379  flags->*t1 = t.get();
380  } else {
381  return Error("Failed to load value '" + value + "': " + t.error());
382  }
383  }
384 
385  return Nothing();
386  };
387 
388  flag.stringify = [t1](const FlagsBase& base) -> Option<std::string> {
389  const Flags* flags = dynamic_cast<const Flags*>(&base);
390  if (flags != nullptr) {
391  return stringify(flags->*t1);
392  }
393  return None();
394  };
395 
396  flag.validate = [t1, validate](const FlagsBase& base) -> Option<Error> {
397  const Flags* flags = dynamic_cast<const Flags*>(&base);
398  if (flags != nullptr) {
399  return validate(flags->*t1);
400  }
401  return None();
402  };
403 
404  // Update the help string to include the default value.
405  flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
406  ? " (default: " // On same line, add space.
407  : "(default: "; // On newline.
408  if (t2 != nullptr) {
409  flag.help += stringify(*t2);
410  }
411  flag.help += ")";
412 
413  add(flag);
414 }
415 
416 
417 template <typename Flags, typename T, typename F>
419  Option<T> Flags::*option,
420  const Name& name,
421  const Option<Name>& alias,
422  const std::string& help,
423  F validate)
424 {
425  // Don't bother adding anything if the pointer is `nullptr`.
426  if (option == nullptr) {
427  return;
428  }
429 
430  Flags* flags = dynamic_cast<Flags*>(this);
431  if (flags == nullptr) {
432  ABORT("Attempted to add flag '" + name.value +
433  "' with incompatible type");
434  }
435 
436  Flag flag;
437  flag.name = name;
438  flag.alias = alias;
439  flag.help = help;
440  flag.boolean = typeid(T) == typeid(bool);
441  flag.required = false;
442 
443  // NOTE: See comment above in Flags::T* overload of FLagsBase::add
444  // for why we need to pass FlagsBase* (or const FlagsBase&) as a
445  // parameter.
446 
447  flag.load =
448  [option](FlagsBase* base, const std::string& value) -> Try<Nothing> {
449  Flags* flags = dynamic_cast<Flags*>(base);
450  if (flags != nullptr) {
451  // NOTE: 'fetch' "retrieves" the value if necessary and then
452  // invokes 'parse'. See 'fetch' for more details.
453  Try<T> t = fetch<T>(value);
454  if (t.isSome()) {
455  flags->*option = Some(t.get());
456  } else {
457  return Error("Failed to load value '" + value + "': " + t.error());
458  }
459  }
460 
461  return Nothing();
462  };
463 
464  flag.stringify = [option](const FlagsBase& base) -> Option<std::string> {
465  const Flags* flags = dynamic_cast<const Flags*>(&base);
466  if (flags != nullptr) {
467  if ((flags->*option).isSome()) {
468  return stringify((flags->*option).get());
469  }
470  }
471  return None();
472  };
473 
474  flag.validate = [option, validate](const FlagsBase& base) -> Option<Error> {
475  const Flags* flags = dynamic_cast<const Flags*>(&base);
476  if (flags != nullptr) {
477  return validate(flags->*option);
478  }
479  return None();
480  };
481 
482  add(flag);
483 }
484 
485 
486 inline void FlagsBase::add(const Flag& flag)
487 {
488  // Check if the name and alias of the flag are valid.
489  std::vector<Name> names = {flag.name};
490  if (flag.alias.isSome()) {
491  if (flag.alias.get() == flag.name) {
492  EXIT(EXIT_FAILURE)
493  << "Attempted to add flag '" << flag.name.value << "' with an alias"
494  << " that is same as the flag name";
495  }
496 
497  names.push_back(flag.alias.get());
498  }
499 
500  foreach (const Name& name, names) {
501  if (flags_.count(name.value) > 0) {
502  EXIT(EXIT_FAILURE)
503  << "Attempted to add duplicate flag '" << name.value << "'";
504  } else if (name.value.find("no-") == 0) {
505  EXIT(EXIT_FAILURE)
506  << "Attempted to add flag '" << name.value
507  << "' that starts with the reserved 'no-' prefix";
508  }
509  }
510 
511  flags_[flag.name.value] = flag;
512  if (flag.alias.isSome()) {
513  aliases[flag.alias->value] = flag.name.value;
514  }
515 }
516 
517 
518 // Extract environment variable "flags" with the specified prefix.
519 inline std::map<std::string, Option<std::string>> FlagsBase::extract(
520  const std::string& prefix) const
521 {
522  std::map<std::string, Option<std::string>> values;
523 
524  foreachpair (const std::string& key,
525  const std::string& value,
526  os::environment()) {
527  if (key.find(prefix) == 0) {
528  std::string name = key.substr(prefix.size());
529  name = strings::lower(name); // Allow PREFIX_NAME or PREFIX_name.
530 
531  // Only add if it's a known flag.
532  // TODO(vinod): Reject flags with an unknown name if `unknowns` is false.
533  // This will break backwards compatibility however!
534  std::string flag_name = strings::remove(name, "no-", strings::PREFIX);
535  if (flags_.count(flag_name) > 0 || aliases.count(flag_name) > 0) {
536  values[name] = Some(value);
537  }
538  }
539  }
540 
541  return values;
542 }
543 
544 
545 inline std::map<std::string, std::string> FlagsBase::buildEnvironment(
546  const Option<std::string>& prefix) const
547 {
548  std::map<std::string, std::string> result;
549 
550  foreachvalue (const Flag& flag, flags_) {
551  Option<std::string> value = flag.stringify(*this);
552  if (value.isSome()) {
553  const std::string key = prefix.isSome()
554  ? prefix.get() + strings::upper(flag.effective_name().value)
556 
557  result[key] = value.get();
558  }
559  }
560 
561  return result;
562 }
563 
564 
565 inline Try<Warnings> FlagsBase::load(const std::string& prefix)
566 {
567  return load(extract(prefix));
568 }
569 
570 
572  const Option<std::string>& prefix,
573  int argc,
574  const char* const *argv,
575  bool unknowns,
576  bool duplicates)
577 {
579 
580  // Grab the program name from argv[0].
581  programName_ = argc > 0 ? Path(argv[0]).basename() : "";
582 
583  // Read flags from the command line.
584  for (int i = 1; i < argc; i++) {
585  const std::string arg(strings::trim(argv[i]));
586 
587  // Stop parsing flags after '--' is encountered.
588  if (arg == "--") {
589  break;
590  }
591 
592  // Skip anything that doesn't look like a flag.
593  if (arg.find("--") != 0) {
594  continue;
595  }
596 
597  std::string name;
598  Option<std::string> value = None();
599 
600  size_t eq = arg.find_first_of('=');
601  if (eq == std::string::npos && arg.find("--no-") == 0) { // --no-name
602  name = arg.substr(2);
603  } else if (eq == std::string::npos) { // --name
604  name = arg.substr(2);
605  } else { // --name=value
606  name = arg.substr(2, eq - 2);
607  value = arg.substr(eq + 1);
608  }
609 
610  name = strings::lower(name);
611 
612  values.put(name, value);
613  }
614 
615  return load(values, unknowns, duplicates, prefix);
616 }
617 
618 
620  const Option<std::string>& prefix,
621  int* argc,
622  char*** argv,
623  bool unknowns,
624  bool duplicates)
625 {
627 
628  // Grab the program name from argv, without removing it.
629  programName_ = *argc > 0 ? Path(*(argv[0])).basename() : "";
630 
631  // Keep the arguments that are not being processed as flags.
632  std::vector<char*> args;
633 
634  // Read flags from the command line.
635  for (int i = 1; i < *argc; i++) {
636  const std::string arg(strings::trim((*argv)[i]));
637 
638  // Stop parsing flags after '--' is encountered.
639  if (arg == "--") {
640  // Save the rest of the arguments.
641  for (int j = i + 1; j < *argc; j++) {
642  args.push_back((*argv)[j]);
643  }
644  break;
645  }
646 
647  // Skip anything that doesn't look like a flag.
648  if (arg.find("--") != 0) {
649  args.push_back((*argv)[i]);
650  continue;
651  }
652 
653  std::string name;
654  Option<std::string> value = None();
655 
656  size_t eq = arg.find_first_of('=');
657  if (eq == std::string::npos && arg.find("--no-") == 0) { // --no-name
658  name = arg.substr(2);
659  } else if (eq == std::string::npos) { // --name
660  name = arg.substr(2);
661  } else { // --name=value
662  name = arg.substr(2, eq - 2);
663  value = arg.substr(eq + 1);
664  }
665 
666  name = strings::lower(name);
667 
668  values.put(name, value);
669  }
670 
671  Try<Warnings> result = load(values, unknowns, duplicates, prefix);
672 
673  // Update 'argc' and 'argv' if we successfully loaded the flags.
674  if (!result.isError()) {
675  CHECK_LE(args.size(), (size_t) *argc);
676  int i = 1; // Start at '1' to skip argv[0].
677  foreach (char* arg, args) {
678  (*argv)[i++] = arg;
679  }
680 
681  *argc = i;
682 
683  // Now null terminate the array. Note that we'll "leak" the
684  // arguments that were processed here but it's not like they would
685  // have gotten deleted in normal operations anyway.
686  (*argv)[i++] = nullptr;
687  }
688 
689  return result;
690 }
691 
692 
694  const std::map<std::string, Option<std::string>>& values,
695  bool unknowns,
696  const Option<std::string>& prefix)
697 {
699  foreachpair (const std::string& name,
700  const Option<std::string>& value,
701  values) {
702  values_.put(name, value);
703  }
704  return load(values_, unknowns, false, prefix);
705 }
706 
707 
709  const std::map<std::string, std::string>& values,
710  bool unknowns,
711  const Option<std::string>& prefix)
712 {
714  foreachpair (const std::string& name, const std::string& value, values) {
715  values_.put(name, Some(value));
716  }
717  return load(values_, unknowns, false, prefix);
718 }
719 
720 
722  Multimap<std::string, Option<std::string>>& values,
723  bool unknowns,
724  bool duplicates,
725  const Option<std::string>& prefix)
726 {
727  Warnings warnings;
728 
729  if (prefix.isSome()) {
730  // Merge in flags from the environment. Values in the
731  // map take precedence over environment flags.
732  //
733  // Other overloads parse command line flags into
734  // the values map and pass them into this method.
735  foreachpair (const std::string& name,
736  const Option<std::string>& value,
737  extract(prefix.get())) {
738  if (!values.contains(name)) {
739  values.put(name, value);
740  }
741  }
742  }
743 
744  foreachpair (const std::string& name,
745  const Option<std::string>& value,
746  values) {
747  bool is_negated = strings::startsWith(name, "no-");
748  std::string flag_name = !is_negated ? name : name.substr(3);
749 
750  auto iter = aliases.count(flag_name)
751  ? flags_.find(aliases[flag_name])
752  : flags_.find(flag_name);
753 
754  if (iter == flags_.end()) {
755  if (!unknowns) {
756  return Error("Failed to load unknown flag '" + flag_name + "'" +
757  (!is_negated ? "" : " via '" + name + "'"));
758  } else {
759  continue;
760  }
761  }
762 
763  Flag* flag = &(iter->second);
764 
765  if (!duplicates && flag->loaded_name.isSome()) {
766  return Error("Flag '" + flag_name + "' is already loaded via name '" +
767  flag->loaded_name->value + "'");
768  }
769 
770  // Validate the flag value.
771  std::string value_;
772  if (!flag->boolean) { // Non-boolean flag.
773  if (is_negated) { // Non-boolean flag cannot be loaded with "no-" prefix.
774  return Error("Failed to load non-boolean flag '" + flag_name +
775  "' via '" + name + "'");
776  }
777 
778  if (value.isNone()) {
779  return Error("Failed to load non-boolean flag '" + flag_name +
780  "': Missing value");
781  }
782 
783  value_ = value.get();
784  } else { // Boolean flag.
785  if (value.isNone() || value.get() == "") {
786  value_ = !is_negated ? "true" : "false";
787  } else if (!is_negated) {
788  value_ = value.get();
789  } else { // Boolean flag with "no-" prefix cannot have non-empty value.
790  return Error(
791  "Failed to load boolean flag '" + flag_name + "' via '" + name +
792  "' with value '" + value.get() + "'");
793  }
794  }
795 
796  Try<Nothing> load = flag->load(this, value_);
797  if (load.isError()) {
798  return Error("Failed to load flag '" + flag_name + "': " + load.error());
799  }
800 
801  // TODO(vinod): Move this logic inside `Flag::load()`.
802 
803  // Set `loaded_name` to the Name corresponding to `flag_name`.
804  if (aliases.count(flag_name)) {
805  CHECK_SOME(flag->alias);
806  flag->loaded_name = flag->alias.get();
807  } else {
808  flag->loaded_name = flag->name;
809  }
810 
811  if (flag->loaded_name->deprecated) {
812  warnings.warnings.push_back(
813  Warning("Loaded deprecated flag '" + flag_name + "'"));
814  }
815  }
816 
817  // Validate the flags value.
818  //
819  // TODO(benh): Consider validating all flags at the same time in
820  // order to provide more feedback rather than requiring a user to
821  // fix one at a time.
822  foreachvalue (const Flag& flag, flags_) {
823  if (flag.required && flag.loaded_name.isNone()) {
824  return Error(
825  "Flag '" + flag.name.value +
826  "' is required, but it was not provided");
827  }
828 
829  Option<Error> error = flag.validate(*this);
830  if (error.isSome()) {
831  return error.get();
832  }
833  }
834 
835  return warnings;
836 }
837 
838 
839 inline std::string FlagsBase::usage(const Option<std::string>& message) const
840 {
841  const int PAD = 5;
842 
843  std::string usage;
844 
845  if (message.isSome()) {
846  usage = message.get() + "\n\n";
847  }
848 
849  if (usageMessage_.isNone()) {
850  usage += "Usage: " + programName_ + " [options]\n\n";
851  } else {
852  usage += usageMessage_.get() + "\n\n";
853  }
854 
855  std::map<std::string, std::string> col1; // key -> col 1 string.
856 
857  // Construct string for the first column and store width of column.
858  size_t width = 0;
859 
860  foreachvalue (const flags::Flag& flag, *this) {
861  if (flag.boolean) {
862  col1[flag.name.value] += " --[no-]" + flag.name.value;
863  if (flag.alias.isSome()) {
864  col1[flag.name.value] += ", --[no-]" + flag.alias->value;
865  }
866  } else {
867  // TODO(vinod): Rename "=VALUE" to "=<VALUE>".
868  col1[flag.name.value] += " --" + flag.name.value + "=VALUE";
869  if (flag.alias.isSome()) {
870  col1[flag.name.value] += ", --" + flag.alias->value + "=VALUE";
871  }
872  }
873  width = std::max(width, col1[flag.name.value].size());
874  }
875 
876  // TODO(vinod): Print the help on the next line instead of on the same line as
877  // the names.
878  foreachvalue (const flags::Flag& flag, *this) {
879  std::string line = col1[flag.name.value];
880 
881  std::string pad(PAD + width - line.size(), ' ');
882  line += pad;
883 
884  size_t pos1 = 0, pos2 = 0;
885  pos2 = flag.help.find_first_of("\n\r", pos1);
886  line += flag.help.substr(pos1, pos2 - pos1) + "\n";
887  usage += line;
888 
889  while (pos2 != std::string::npos) { // Handle multi-line help strings.
890  line = "";
891  pos1 = pos2 + 1;
892  std::string pad2(PAD + width, ' ');
893  line += pad2;
894  pos2 = flag.help.find_first_of("\n\r", pos1);
895  line += flag.help.substr(pos1, pos2 - pos1) + "\n";
896  usage += line;
897  }
898  }
899 
900  return usage;
901 }
902 
903 
904 inline std::ostream& operator<<(std::ostream& stream, const FlagsBase& flags)
905 {
906  std::vector<std::string> _flags;
907 
908  foreachvalue (const flags::Flag& flag, flags) {
909  const Option<std::string> value = flag.stringify(flags);
910  if (value.isSome()) {
911  _flags.push_back("--" + flag.effective_name().value + "=\"" +
912  value.get() + '"');
913  }
914  }
915 
916  return stream << strings::join(" ", _flags);
917 }
918 
919 } // namespace flags {
920 
921 #endif // __STOUT_FLAGS_FLAGS_HPP__
bool boolean
Definition: flag.hpp:89
void add(Option< T > Flags::*option, const Name &name, const Option< Name > &alias, const std::string &help)
Definition: flags.hpp:280
Definition: nothing.hpp:16
Option< Error > validate(const std::string &imageDir)
Definition: errorbase.hpp:36
std::vector< Warning > warnings
Definition: flag.hpp:73
#define ABORT(...)
Definition: abort.hpp:40
void add(T1 Flags::*t1, const Name &name, const Option< Name > &alias, const std::string &help, const T2 &t2, F validate)
Definition: flags.hpp:195
T & get()&
Definition: try.hpp:73
std::stringstream & join(std::stringstream &stream, const std::string &separator, T &&...args)
Definition: strings.hpp:307
Definition: check.hpp:33
constexpr const char * prefix
Definition: os.hpp:94
iterator begin()
Definition: flags.hpp:176
FlagsBase()
Definition: flags.hpp:47
iterator end()
Definition: flags.hpp:177
#define EXIT(status)
Definition: exit.hpp:31
event_base * base
const_iterator begin() const
Definition: flags.hpp:171
bool isSome() const
Definition: option.hpp:115
std::string remove(const std::string &from, const std::string &substring, Mode mode=ANY)
Definition: strings.hpp:41
std::map< std::string, std::string > buildEnvironment(const Option< std::string > &prefix=None()) const
Definition: flags.hpp:545
Definition: flag.hpp:61
std::string programName_
Definition: flags.hpp:309
#define CHECK_SOME(expression)
Definition: check.hpp:50
std::map< std::string, Flag >::iterator iterator
Definition: flags.hpp:174
std::string help
Definition: flag.hpp:88
Represents a POSIX or Windows file system path and offers common path manipulations.
Definition: path.hpp:145
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:208
std::map< std::string, Option< std::string > > extract(const std::string &prefix) const
Definition: flags.hpp:519
Option< std::string > usageMessage_
Definition: flags.hpp:315
Definition: flag.hpp:77
Definition: flags.hpp:44
bool isSome() const
Definition: try.hpp:70
void setUsageMessage(const std::string &message)
Definition: flags.hpp:164
const T & get() const &
Definition: option.hpp:118
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
virtual ~FlagsBase()=default
Option< Name > alias
Definition: flag.hpp:80
void add(Option< T > Flags::*option, const Name &name, const std::string &help)
Definition: flags.hpp:271
std::map< std::string, std::string > environment()
Definition: environment.hpp:24
Name name
Definition: flag.hpp:79
lambda::function< Try< Nothing >FlagsBase *, const std::string &)> load
Definition: flag.hpp:90
#define foreachvalue(VALUE, ELEMS)
Definition: foreach.hpp:77
bool required
Definition: flag.hpp:93
virtual Try< Warnings > load(const std::string &prefix)
Definition: flags.hpp:565
std::string usage(const Option< std::string > &message=None()) const
Definition: flags.hpp:839
static Try error(const E &e)
Definition: try.hpp:42
Definition: multimap.hpp:30
std::map< std::string, Flag >::const_iterator const_iterator
Definition: flags.hpp:169
lambda::function< Option< Error >const FlagsBase &)> validate
Definition: flag.hpp:92
std::string value
Definition: flag.hpp:47
std::string upper(const std::string &s)
Definition: strings.hpp:437
_Some< typename std::decay< T >::type > Some(T &&t)
Definition: some.hpp:42
bool help
Definition: flags.hpp:295
Iterable< V > map(F &&f, const Iterable< U, Us... > &input)
Definition: lambda.hpp:46
Definition: flag.hpp:71
Definition: none.hpp:27
bool isError() const
Definition: try.hpp:71
std::ostream & operator<<(std::ostream &stream, const SecurePathOrValue &flag)
Definition: flag.hpp:113
std::string error(const std::string &msg, uint32_t code)
void add(T Flags::*t, const Name &name, const std::string &help)
Definition: flags.hpp:228
void put(const K &key, const V &value)
Definition: multimap.hpp:53
void add(T1 Flags::*t1, const Name &name, const Option< Name > &alias, const std::string &help, const T2 *t2, F validate)
Definition: flags.hpp:333
void add(T1 Flags::*t1, const Name &name, const Option< Name > &alias, const std::string &help, const T2 &t2)
Definition: flags.hpp:242
bool isNone() const
Definition: option.hpp:116
Option< Name > loaded_name
Definition: flag.hpp:86
lambda::function< Option< std::string >const FlagsBase &)> stringify
Definition: flag.hpp:91
void add(T1 Flags::*t1, const Name &name, const std::string &help, const T2 &t2, F validate)
Definition: flags.hpp:207
std::string basename() const
Extracts the component following the final &#39;/&#39;.
Definition: path.hpp:179
Definition: flag.hpp:31
std::string stringify(int flags)
bool startsWith(const std::string &s, const std::string &prefix)
Definition: strings.hpp:381
FlagsBase & operator=(const FlagsBase &)=default
void add(Option< T > Flags::*option, const Name &name, const std::string &help, F validate)
Definition: flags.hpp:261
const Name & effective_name() const
Definition: flag.hpp:97
Definition: parse.hpp:33
std::string lower(const std::string &s)
Definition: strings.hpp:429
const_iterator end() const
Definition: flags.hpp:172
constexpr const char * name
Definition: shell.hpp:43
std::string trim(const std::string &from, Mode mode=ANY, const std::string &chars=WHITESPACE)
Definition: strings.hpp:67
void add(T1 Flags::*t1, const Name &name, const std::string &help, const T2 &t2)
Definition: flags.hpp:218
Definition: strings.hpp:35