Merge pull request #140 from hussainnaqvee/patch-1
[awesomized/libmemcached] / src / bin / common / options.hpp
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
14 */
15
16 #pragma once
17
18 #include <algorithm>
19 #include <cstdint>
20 #include <climits>
21 #include <functional>
22 #include <iostream>
23 #include <string>
24 #include <vector>
25
26 #include "libmemcached/common.h"
27 #include "p9y/getopt.hpp"
28
29 class client_options {
30 public:
31
32 struct extended_option {
33 option opt;
34 std::string help;
35 std::function<bool(client_options &, extended_option &)> parse;
36 std::function<bool(const client_options &, const extended_option &, memcached_st *)> apply;
37 char *arg;
38 bool set;
39 };
40
41 std::vector<extended_option> options;
42 std::vector<extended_option> defaults;
43
44 const char *prog_name;
45 const char *prog_vers;
46 const char *prog_desc;
47 const char *prog_argp;
48
49 client_options(const char *prg, const char *ver, const char *dsc, const char *arg = nullptr)
50 : options{}
51 , defaults{}
52 , prog_name{prg}
53 , prog_vers{ver}
54 , prog_desc{dsc}
55 , prog_argp{arg}
56 {
57
58 def("help", 'h', no_argument, "Print this help.")
59 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
60 if (ext.set) {
61 opt.print_help();
62 exit(EXIT_SUCCESS);
63 }
64 return true;
65 };
66 def("version", 'V', no_argument, "Print program version.")
67 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
68 if (ext.set) {
69 opt.print_version();
70 exit(EXIT_SUCCESS);
71 }
72 return true;
73 };
74
75 def("verbose", 'v', no_argument, "Print more informational output.")
76 .parse = [](client_options &opt, extended_option &) {
77 opt.unset("quiet");
78 return true;
79 };
80 def("debug", 'd', no_argument, "Print output useful only for debugging.")
81 .parse = [](client_options &opt, extended_option &) {
82 opt.set("verbose");
83 opt.unset("quiet");
84 return true;
85 };
86 def("quiet", 'q', no_argument, "Print no output, not even errors.")
87 .parse = [](client_options &opt, extended_option &) {
88 opt.unset("verbose");
89 opt.unset("debug");
90 return true;
91 };
92
93 def("password", 'p', required_argument, "SASL password.");
94 def("username", 'u', required_argument, "SASL username.")
95 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
96 if (auto username = ext.arg) {
97 #if !LIBMEMCACHED_WITH_SASL_SUPPORT
98 (void) memc;
99 if (!opt.isset("quiet")) {
100 std::cerr
101 << "SASL username '" << username << "' was supplied, but binary was not built with SASL support.\n";
102 }
103 return false;
104 #else
105 if (memc) {
106 if (MEMCACHED_SUCCESS
107 != memcached_set_sasl_auth_data(memc, username, opt.argof("password"))) {
108 if (!opt.isset("quiet")) {
109 std::cerr << memcached_last_error_message(memc);
110 }
111 return false;
112 }
113 }
114 #endif
115 }
116 return true;
117 };
118
119 def("binary", 'b', no_argument, "Use the binary memcached protocol.")
120 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
121 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, ext.set)) {
122 if(!opt.isset("quiet")) {
123 std::cerr << memcached_last_error_message(memc);
124 }
125 return false;
126 }
127 return true;
128 };
129 def("buffer", 'B', no_argument, "Buffer requests.")
130 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
131 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, ext.set)) {
132 if(!opt.isset("quiet")) {
133 std::cerr << memcached_last_error_message(memc);
134 }
135 return false;
136 }
137 return true;
138 };
139 def("non-blocking", 'n', no_argument, "Use non-blocking connections.")
140 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
141 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, ext.set)) {
142 if(!opt.isset("quiet")) {
143 std::cerr << memcached_last_error_message(memc);
144 }
145 return false;
146 }
147 return true;
148 };
149 def("tcp-nodelay", 'N', no_argument, "Disable Nagle's algorithm.")
150 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
151 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, ext.set)) {
152 if(!opt.isset("quiet")) {
153 std::cerr << memcached_last_error_message(memc);
154 }
155 return false;
156 }
157 return true;
158 };
159 def("servers", 's', required_argument, "List of servers to connect to.")
160 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
161 auto servers = ext.arg;
162 if (!servers) {
163 if (opt.isset("verbose")) {
164 std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
165 }
166 servers = getenv("MEMCACHED_SERVERS");
167 if (!servers || !*servers) {
168 if (!opt.isset("quiet")) {
169 std::cerr << "No servers provided.\n";
170 }
171 return false;
172 }
173 }
174
175 auto server_list = memcached_servers_parse(servers);
176 if (!server_list || !memcached_server_list_count(server_list)) {
177 if (!opt.isset("quiet")) {
178 std::cerr << "Invalid server list provided: '" << servers << "'\n";
179 }
180 if (server_list) {
181 memcached_server_list_free(server_list);
182 }
183 return false;
184 }
185
186 if (MEMCACHED_SUCCESS != memcached_server_push(memc, server_list)) {
187 if (!opt.isset("quiet")) {
188 std::cerr << memcached_last_error_message(memc);
189 }
190 memcached_server_list_free(server_list);
191 return false;
192 }
193 memcached_server_list_free(server_list);
194 return true;
195 };
196 def("hash", 'H', required_argument, "Key hashing method.")
197 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
198 if (ext.set) {
199 std::string hash_wanted{ext.arg};
200 memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
201
202 std::transform(hash_wanted.begin(), hash_wanted.end(), hash_wanted.begin(), ::toupper);
203
204 if (opt.isset("verbose")) {
205 std::cerr << "Checking for hash '" << hash_wanted << "'.\n";
206 }
207 for (int h = MEMCACHED_HASH_DEFAULT; h < MEMCACHED_HASH_MAX; ++h) {
208 auto hash_type = static_cast<memcached_hash_t>(h);
209 std::string hash_string{libmemcached_string_hash(hash_type)};
210
211 if (hash_wanted.length() == hash_string.length()) {
212 auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(),
213 [](int a, int b) { return ::toupper(a) == b; });
214 if (ci) {
215 hash = hash_type;
216 break;
217 }
218 }
219 }
220 if (hash == MEMCACHED_HASH_DEFAULT) {
221 if (!opt.isset("quiet")) {
222 std::cerr << "Could not find hash '" << hash_wanted << "'.\n";
223 }
224 }
225 if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, hash)) {
226 if (!opt.isset("quiet")) {
227 std::cerr << memcached_last_error_message(memc);
228 }
229 return false;
230 }
231 }
232 return true;
233 };
234 }
235
236 extended_option &def(option opt, std::string help) {
237 defaults.emplace_back(extended_option{opt, std::move(help), {}, {}, nullptr, false});
238 return defaults.back();
239 }
240
241 extended_option &def(const char *name, char flag, int has_arg, const char *help) {
242 return def(option{name, has_arg, nullptr, flag}, help);
243 }
244
245 extended_option &add(extended_option ext) {
246 options.emplace_back(std::move(ext));
247 return options.back();
248 }
249
250 extended_option &add(option opt, std::string help) {
251 options.emplace_back(extended_option{opt, std::move(help), nullptr, nullptr, nullptr, false});
252 return options.back();
253 }
254
255 extended_option &add(const char *name, char flag, int has_arg, const char *help) {
256 return add(option{name, has_arg, nullptr, flag}, help);
257 }
258
259 extended_option &get(const std::string &name) {
260 // UB if not found
261 return *find(name);
262 }
263 extended_option &get(int c) {
264 // UB if not found
265 return *find(c);
266 }
267
268 const extended_option &get(const std::string &name) const {
269 // UB if not found
270 return *find(name);
271 }
272 const extended_option &get(int c) const {
273 // UB if not found
274 return *find(c);
275 }
276
277 bool has(const std::string &name) const {
278 auto found = find(name);
279 return found != options.cend();
280 }
281 bool has(int c) const {
282 auto found = find(c);
283 return found != options.cend();
284 }
285
286 bool isset(const std::string &name) const {
287 return has(name) && get(name).set;
288 }
289 bool isset(int c) const {
290 return has(c) && get(c).set;
291 }
292
293 void unset(const std::string &name) {
294 set(name, false);
295 }
296 void unset(int c) {
297 set(c, false);
298 }
299
300 void set(const std::string &name, bool set_ = true, char *optarg_ = nullptr) {
301 if (has(name)) {
302 auto &opt = get(name);
303 opt.set = set_;
304 opt.arg = optarg_;
305 }
306 }
307 void set(int c, bool set_ = true, char *optarg_ = nullptr) {
308 if (has(c)) {
309 auto &opt = get(c);
310 opt.set = set_;
311 opt.arg = optarg_;
312 }
313 }
314
315 char *argof(const std::string &name) const {
316 if (has(name)) {
317 return get(name).arg;
318 }
319 return nullptr;
320 }
321 char *argof(int c) const {
322 if (has(c)) {
323 return get(c).arg;
324 }
325 return nullptr;
326 }
327
328 const extended_option &operator[](const std::string &name) const {
329 return get(name);
330 }
331 const extended_option &operator[](int c) const {
332 return get(c);
333 }
334
335 void print_version() const;
336 void print_help() const;
337
338 bool parse(int argc, char *argv[], char ***argp = nullptr);
339 bool apply(memcached_st *memc);
340
341 private:
342 using iterator = std::vector<extended_option>::iterator;
343 using const_iterator = std::vector<extended_option>::const_iterator;
344 using predicate = std::function<bool(const extended_option &ext)>;
345
346 const_iterator find(const predicate &pred) const {
347 return std::find_if(options.cbegin(), options.cend(), pred);
348 }
349 const_iterator find(const std::string &name) const {
350 return find([&name](const extended_option &ext) {
351 return ext.opt.name && ext.opt.name == name;
352 });
353 }
354 const_iterator find(int c) const {
355 return find([c](const extended_option &ext) {
356 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
357 });
358 }
359
360 iterator find(const predicate &pred) {
361 return std::find_if(options.begin(), options.end(), pred);
362 }
363 iterator find(const std::string &name) {
364 return find([&name](const extended_option &ext) {
365 return ext.opt.name && ext.opt.name == name;
366 });
367 }
368 iterator find(int c) {
369 return find([c](const extended_option &ext) {
370 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
371 });
372 }
373 };