YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/tools/tool_arguments.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#ifndef YB_TOOLS_TOOL_ARGUMENTS_H
15
#define YB_TOOLS_TOOL_ARGUMENTS_H
16
17
#include <boost/preprocessor/seq/for_each.hpp>
18
#include <boost/program_options.hpp>
19
20
#include "yb/util/result.h"
21
22
// Framework for creating command line tool with multiple subcommands.
23
// Usage:
24
// 1) Define actions list for this tool:
25
// #define MY_TOOL_ACTIONS (Help)(Action1)(Action2)
26
// 2) Create enum for actions:
27
// YB_DEFINE_ENUM(MyToolAction, MY_TOOL_ACTIONS);
28
// 3) For each action define required types and functions:
29
// 3a) Action arguments:
30
// struct ActionArguments {
31
//   int arg1;
32
//   std::vector<std::string> arg2;
33
// };
34
// 3b) Action arguments options:
35
// const std::string kAction1Description = "Here goes description for action1";
36
// std::unique_ptr<OptionsDescription> Action1Options() {
37
//   auto result = std::make_unique<OptionsDescriptionImpl<Action1Arguments>>(
38
//       kAction1Description);
39
//   result->desc.add_options()
40
//       ("arg1", boost::program_options::value(&result->args.arg1)->required(),
41
//        "Required argument")
42
//       ("arg2", boost::program_options::value(&result->args.arg2), "Repeatable argument");
43
//   return result;
44
// }
45
// 3c) Action implementation:
46
// CHECKED_STATUS ApplyPatchExecute(const ApplyPatchArguments& args) {
47
//   ApplyPatch apply_patch;
48
//   return apply_patch.Execute(args);
49
// }
50
// 4) Bind actions with framework: YB_TOOL_ARGUMENTS(MyToolAction, MY_TOOL_ACTIONS);
51
// 5) Run framework from main function:
52
// return yb::tools::ExecuteTool<yb::tools::MyToolAction>(argc, argv);
53
54
namespace yb {
55
namespace tools {
56
57
template <class Action>
58
0
std::string OptionName(Action action) {
59
0
  auto name = ToString(action);
60
0
  std::string result;
61
0
  for (char ch : name) {
62
0
    if (std::isupper(ch)) {
63
0
      if (!result.empty()) {
64
0
        result += '-';
65
0
      }
66
0
      result += std::tolower(ch);
67
0
    } else {
68
0
      result += ch;
69
0
    }
70
0
  }
71
0
  return result;
72
0
}
73
74
template <class Action>
75
0
Result<Action> ActionByName(const std::string& name) {
76
0
  for (auto action : List(static_cast<Action*>(nullptr))) {
77
0
    if (OptionName(action) == name) {
78
0
      return action;
79
0
    }
80
0
  }
81
0
  return STATUS_FORMAT(InvalidArgument, "Unknown command: $0", name);
82
0
}
83
84
template <class Action>
85
0
void ShowHelp(Action action) {
86
0
  auto options = CreateOptionsDescription(action);
87
0
  std::cout << options->desc << std::endl;
88
0
}
89
90
template <class Action>
91
0
void ShowCommands() {
92
0
  std::cout << "Commands:" << std::endl;
93
0
  size_t max_name_len = 0;
94
0
  for (auto action : List(static_cast<Action*>(nullptr))) {
95
0
    max_name_len = std::max(max_name_len, OptionName(action).size());
96
0
  }
97
0
  max_name_len += 3;
98
0
  for (auto action : List(static_cast<Action*>(nullptr))) {
99
0
    auto current_name = OptionName(action);
100
0
    std::cout << "  " << current_name << std::string(max_name_len - current_name.length(), ' ')
101
0
              << Description(action) << std::endl;
102
0
  }
103
0
}
104
105
struct OptionsDescription {
106
  boost::program_options::positional_options_description positional;
107
  boost::program_options::options_description desc;
108
  boost::program_options::options_description hidden;
109
110
0
  explicit OptionsDescription(const std::string& caption) : desc(caption) {}
111
0
  virtual ~OptionsDescription() = default;
112
};
113
114
template<class Arguments>
115
struct OptionsDescriptionImpl : OptionsDescription {
116
  Arguments args;
117
118
0
  explicit OptionsDescriptionImpl(const std::string& caption) : OptionsDescription(caption) {}
Unexecuted instantiation: _ZN2yb5tools22OptionsDescriptionImplINS0_13HelpArgumentsEEC2ERKNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE
Unexecuted instantiation: _ZN2yb5tools22OptionsDescriptionImplINS0_19ChangeTimeArgumentsEEC2ERKNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE
Unexecuted instantiation: _ZN2yb5tools22OptionsDescriptionImplINS0_19ApplyPatchArgumentsEEC2ERKNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEE
119
};
120
121
#define YB_TOOL_ARGUMENTS_DESCRIPTION(x, enum_name, elem) \
122
0
  case enum_name::elem: return BOOST_PP_CAT(k, BOOST_PP_CAT(elem, Description));
123
124
#define YB_TOOL_ARGUMENTS_CREATE_OPTIONS(x, enum_name, elem) \
125
0
  case enum_name::elem: return BOOST_PP_CAT(elem, Options)();
126
127
#define YB_TOOL_ARGUMENTS_EXECUTE(x, enum_name, elem) \
128
0
  case enum_name::elem: \
129
0
    return BOOST_PP_CAT(elem, Execute)( \
130
0
        static_cast<const OptionsDescriptionImpl<BOOST_PP_CAT(elem, Arguments)>&>( \
131
0
            description).args);
132
133
#define YB_TOOL_ARGUMENTS(enum_name, actions) \
134
0
  const std::string& Description(enum_name action) { \
135
0
    switch (action) { \
136
0
      BOOST_PP_SEQ_FOR_EACH(YB_TOOL_ARGUMENTS_DESCRIPTION, enum_name, actions); \
137
0
    } \
138
0
    FATAL_INVALID_ENUM_VALUE(enum_name, action); \
139
0
  } \
140
0
  std::unique_ptr<OptionsDescription> CreateOptionsDescription(enum_name action) { \
141
0
    switch (action) { \
142
0
      BOOST_PP_SEQ_FOR_EACH(YB_TOOL_ARGUMENTS_CREATE_OPTIONS, enum_name, actions); \
143
0
    } \
144
0
    FATAL_INVALID_ENUM_VALUE(enum_name, action); \
145
0
  } \
146
0
  CHECKED_STATUS Execute(enum_name action, const OptionsDescription& description) { \
147
0
    switch (action) { \
148
0
      BOOST_PP_SEQ_FOR_EACH(YB_TOOL_ARGUMENTS_EXECUTE, enum_name, actions); \
149
0
    } \
150
0
    FATAL_INVALID_ENUM_VALUE(enum_name, action); \
151
0
  }
152
153
0
inline std::string GetCommand(int argc, char** argv) {
154
0
  return argc < 2 ? "" : argv[1];
155
0
}
156
157
template <class Action>
158
0
int ExecuteTool(int argc, char** argv) {
159
0
  std::string cmd = GetCommand(argc, argv);
160
0
  if (cmd.empty()) {
161
0
    ShowCommands<Action>();
162
0
    return 0;
163
0
  }
164
165
0
  auto action = ActionByName<Action>(cmd);
166
0
  if (!action.ok()) {
167
0
    std::cerr << action.status().message().ToBuffer() << std::endl;
168
0
    return 1;
169
0
  }
170
171
0
  try {
172
0
    auto options = CreateOptionsDescription(*action);
173
0
    boost::program_options::command_line_parser parser(argc - 1, argv + 1);
174
0
    boost::program_options::options_description descriptions;
175
0
    descriptions.add(options->desc);
176
0
    descriptions.add(options->hidden);
177
0
    parser.options(descriptions).positional(options->positional);
178
0
    auto parsed = parser.run();
179
0
    boost::program_options::variables_map variables_map;
180
0
    boost::program_options::store(parsed, variables_map);
181
0
    boost::program_options::notify(variables_map);
182
0
    auto status = Execute(*action, *options);
183
0
    if (!status.ok()) {
184
0
      std::cerr << status.message().ToBuffer() << std::endl;
185
0
      return 1;
186
0
    }
187
0
    return 0;
188
0
  } catch (std::exception& exc) {
189
0
    std::cerr << exc.what() << std::endl;
190
0
    ShowHelp(*action);
191
0
    return 1;
192
0
  }
193
194
0
  return 0;
195
0
}
196
197
} // namespace tools
198
} // namespace yb
199
200
#endif // YB_TOOLS_TOOL_ARGUMENTS_H