SNode.C
Loading...
Searching...
No Matches
SubCommand.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025, 2026
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "SubCommand.h"
43
44#include "utils/Formatter.h"
45
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
47
48#include <cstddef>
49#include <cstdint>
50#include <memory>
51#include <vector>
52
53#endif // DOXYGEN_SHOULD_SKIP_THIS
54
55namespace utils {
56
57 SubCommand::SubCommand(SubCommand* parent, std::shared_ptr<utils::AppWithPtr> appWithPtr, const std::string& group, bool final)
58 : subCommandApp(appWithPtr.get())
59 , parent(parent)
60 , final(final) {
61 if (appWithPtr != nullptr) {
62 if (parent != nullptr) {
63 parent->subCommandApp->add_subcommand(appWithPtr);
64 }
65
66 subCommandApp->group(group);
67 subCommandApp->configurable(false);
68 subCommandApp->allow_extras();
69
70 static const std::shared_ptr<CLI::HelpFormatter> helpFormatter = std::make_shared<CLI::HelpFormatter>();
71
72 helpFormatter->label("SUBCOMMAND", "INSTANCE");
73 helpFormatter->label("SUBCOMMANDS", "INSTANCES");
74 helpFormatter->label("PERSISTENT", "");
75 helpFormatter->label("Persistent Options", "Options (persistent)");
76 helpFormatter->label("Nonpersistent Options", "Options (nonpersistent)");
77 helpFormatter->label("Usage", "\nUsage");
78 helpFormatter->label("bool:{true,false}", "{true,false}");
79 helpFormatter->label(":{standard,active,complete,required}", "{standard,active,complete,required}");
80 helpFormatter->label(":{standard,exact,expanded}", "{standard,exact,expanded}");
81 helpFormatter->column_width(7);
82
83 subCommandApp->formatter(helpFormatter);
84
85 static const std::shared_ptr<CLI::Config> configFormatter = std::make_shared<CLI::ConfigFormatter>();
86
87 subCommandApp->config_formatter(configFormatter);
88
89 subCommandApp->option_defaults()->take_last()->group(subCommandApp->get_formatter()->get_label("Nonpersistent Options"));
90
91 if (!final) {
93 ->set_help_flag(
94 "--help{exact},-h{exact}",
95 [subCommandApp = this->subCommandApp](std::size_t) {
96 helpTriggerApp = subCommandApp;
97 },
98 "Print help message and exit\n"
99 "* standard: display help for the last command processed\n"
100 "* exact: display help for the command directly preceding --help")
101 ->take_first()
102 ->check(CLI::IsMember({"standard", "exact", "expanded"}))
103 ->trigger_on_parse(),
104 false);
105 } else {
107 ->set_help_flag(
108 "--help{exact},-h{exact}",
109 [subCommandApp = this->subCommandApp](std::size_t) {
110 helpTriggerApp = subCommandApp;
111 },
112 "Print help message and exit\n"
113 "* standard: display help for the last command processed\n"
114 "* exact: display help for the command directly preceding --help")
115 ->take_first()
116 ->check(CLI::IsMember({"standard", "exact"}))
117 ->trigger_on_parse(),
118 false);
119 }
120
122 ->add_flag_function(
123 "-s,--show-config",
124 [subCommandApp = this->subCommandApp](std::size_t) {
125 showConfigTriggerApp = subCommandApp;
126 },
127 "Show current configuration and exit")
128 ->take_first()
129 ->disable_flag_override()
130 ->configurable(false)
131 ->trigger_on_parse(),
132 false);
133
135 ->add_flag_function(
136 "--command-line{standard}",
137 [subCommandApp = this->subCommandApp]([[maybe_unused]] std::int64_t count) {
138 commandlineTriggerApp = subCommandApp;
139 },
140 "Print command-line\n"
141 "* standard (default): Show all non-default and required options\n"
142 "* active: Show all active options\n"
143 "* complete: Show the complete option set with default values\n"
144 "* required: Show only required options")
145 ->take_first()
146 ->check(CLI::IsMember({"standard", "active", "complete", "required"}))
147 ->trigger_on_parse(),
148 false);
149 }
150 }
151
154
155 std::string SubCommand::getName() const {
156 return subCommandApp->get_name();
157 }
158
159 std::string SubCommand::version() const {
160 return subCommandApp->version();
161 }
162
163 void SubCommand::parse(int argc, char* argv[]) {
164 subCommandApp->parse(argc, argv);
165 }
166
167 SubCommand* SubCommand::description(const std::string& description) {
168 subCommandApp->description(description);
169
170 return this;
171 }
172
173 SubCommand* SubCommand::footer(const std::string& footer) {
174 subCommandApp->footer(footer);
175
176 return this;
177 }
178
179 CLI::Option* SubCommand::setConfig(const std::string& defaultConfigFile) const {
180 return subCommandApp
181 ->set_config( //
182 "-c,--config-file",
183 defaultConfigFile,
184 "Read a config file",
185 false) //
186 ->take_all()
187 ->type_name("configfile")
188 ->check(!CLI::ExistingDirectory);
189 }
190
191 CLI::Option* utils::SubCommand::setLogFile(const std::string& defaultLogFile) const {
192 return addOption("-l,--log-file", "Log file", "logFile", defaultLogFile, !CLI::ExistingDirectory);
193 }
194
195 CLI::Option* SubCommand::setVersionFlag(const std::string& version) const {
196 return subCommandApp->set_version_flag("-v,--version", version, "Framework version");
197 }
198
199 bool SubCommand::hasParent() const {
200 return subCommandApp->get_parent() != nullptr;
201 }
202
204 return parent;
205 }
206
207 SubCommand* SubCommand::setRequireCallback(const std::function<void()>& callback) {
208 subCommandApp->require_callback(callback);
209
210 return this;
211 }
212
214 subCommandApp->allow_extras(allow);
215
216 return this;
217 }
218
222
226
228 return helpTriggerApp;
229 }
230
231 SubCommand* SubCommand::required(bool required, bool force) {
232 requiredCount += required ? 1 : -1;
233
234 required = force ? required : requiredCount > 0;
235
236 subCommandApp->required(required);
237
238 SubCommand* parent = getParent();
239 if (parent != nullptr) {
240 if (required) {
241 parent->needs(this);
242 }
243
244 parent->required(this, required);
245 }
246
247 return this;
248 }
249
250 SubCommand* SubCommand::required(SubCommand* subCommand, bool required) {
251 if (subCommandApp->get_required() != required) {
252 if (required) {
253 needs(subCommand);
254
255 for (const auto& sub : subCommand->subCommandApp->get_subcommands([](const CLI::App* sc) -> bool {
256 return sc->get_required();
257 })) {
258 subCommand->subCommandApp->needs(sub);
259 }
260 } else {
261 needs(subCommand, false);
262
263 for (const auto& sub : subCommand->subCommandApp->get_subcommands([](const CLI::App* sc) -> bool {
264 return sc->get_required();
265 })) {
266 subCommand->subCommandApp->remove_needs(sub);
267 }
268 }
269
270 this->required(required, false);
271
272 subCommand->subCommandApp->required(required);
273 subCommand->subCommandApp->ignore_case(required);
274 }
275
276 return this;
277 }
278
279 SubCommand* SubCommand::required(CLI::Option* option, bool required) {
280 if (option->get_required() != required) {
281 option->required(required);
282
283 if (required) {
284 subCommandApp->needs(option);
285 option->default_str("");
286 } else {
287 subCommandApp->remove_needs(option);
288 }
289
290 this->required(required, false);
291 }
292
293 return this;
294 }
295
296 SubCommand* SubCommand::needs(SubCommand* subCommand, bool needs) {
297 if (needs) {
298 subCommandApp->needs(subCommand->subCommandApp);
299 } else {
300 subCommandApp->remove_needs(subCommand->subCommandApp);
301 }
302
303 return this;
304 }
305
306 SubCommand* SubCommand::disabled(SubCommand* subCommand, bool disabled) {
307 if (disabled) {
308 if (subCommand->subCommandApp->get_ignore_case()) {
309 needs(subCommand, false);
310 }
311
312 for (const auto& sub : subCommand->subCommandApp->get_subcommands({})) {
313 if (sub->get_ignore_case()) {
314 subCommand->subCommandApp->remove_needs(sub);
315 sub->required(false);
316 }
317 }
318 } else {
319 if (subCommand->subCommandApp->get_ignore_case()) {
320 needs(subCommand);
321 }
322
323 for (const auto& sub : subCommand->subCommandApp->get_subcommands({})) {
324 if (sub->get_ignore_case()) {
325 subCommand->subCommandApp->needs(sub);
326 sub->required();
327 }
328 }
329 }
330
331 subCommand->subCommandApp->required(disabled ? false : subCommand->subCommandApp->get_ignore_case());
332
333 return this;
334 }
335
336 SubCommand* SubCommand::finalCallback(const std::function<void()>& finalCallback) {
337 subCommandApp->final_callback(finalCallback);
338
339 return this;
340 }
341
342 std::string SubCommand::configToStr() const {
343 return subCommandApp->config_to_str(true, true);
344 }
345
346 std::string SubCommand::help(const CLI::App* helpApp, const CLI::AppFormatMode& mode) const {
347 return subCommandApp->help(helpApp, "", mode);
348 }
349
350 static std::shared_ptr<CLI::HelpFormatter> makeSectionFormatter() {
351 const std::shared_ptr<CLI::HelpFormatter> sectionFormatter = std::make_shared<CLI::HelpFormatter>();
352
353 sectionFormatter->label("SUBCOMMAND", "SECTION");
354 sectionFormatter->label("SUBCOMMANDS", "SECTIONS");
355 sectionFormatter->label("PERSISTENT", "");
356 sectionFormatter->label("Persistent Options", "Options (persistent)");
357 sectionFormatter->label("Nonpersistent Options", "Options (nonpersistent)");
358 sectionFormatter->label("Usage", "\nUsage");
359 sectionFormatter->label("bool:{true,false}", "{true,false}");
360 sectionFormatter->label(":{standard,active,complete,required}", "{standard,active,complete,required}");
361 sectionFormatter->label(":{standard,exact,expanded}", "{standard,exact,expanded}");
362 sectionFormatter->column_width(7);
363
364 return sectionFormatter;
365 }
366
367 std::shared_ptr<CLI::Formatter> SubCommand::sectionFormatter = makeSectionFormatter();
368
369 CLI::Option* SubCommand::getOption(const std::string& name) const {
370 return subCommandApp->get_option_no_throw(name);
371 }
372
373 CLI::Option* SubCommand::addOption(const std::string& name,
374 const std::string& description,
375 const std::string& typeName,
376 const CLI::Validator& validator) const {
378 ->add_option(name, description),
379 typeName,
380 validator,
381 !subCommandApp->get_disabled());
382 }
383
384 CLI::Option* SubCommand::addOptionFunction(const std::string& name,
385 std::function<void(const std::string&)>& callback,
386 const std::string& description,
387 const std::string& typeName,
388 const CLI::Validator& validator) const {
390 ->add_option_function(name, callback, description),
391 typeName,
392 validator,
393 !subCommandApp->get_disabled());
394 }
395
396 CLI::Option* SubCommand::addFlag(const std::string& name,
397 const std::string& description,
398 const std::string& typeName,
399 const CLI::Validator& validator) const {
401 ->add_flag(name, description),
402 typeName,
403 validator,
404 !subCommandApp->get_disabled());
405 }
406
407 CLI::Option* SubCommand::addFlagFunction(const std::string& name,
408 const std::function<void()>& callback,
409 const std::string& description,
410 const std::string& typeName,
411 const CLI::Validator& validator) {
412 CLI::Option* opt = subCommandApp //
413 ->add_flag_function(
414 name,
415 [callback](std::int64_t) {
416 callback();
417 },
418 description)
419 ->type_name(typeName)
420 ->check(validator)
421 ->take_last();
422 if (opt->get_configurable()) {
423 opt->group(subCommandApp->get_formatter()->get_label("Persistent Options"));
424 }
425
426 return opt;
427 }
428
429 CLI::Option* SubCommand::addFlagFunction(const std::string& name,
430 const std::function<void()>& callback,
431 const std::string& description,
432 const std::string& typeName,
433 const std::string& defaultValue,
434 const CLI::Validator& validator) {
435 return setDefaultValue(addFlagFunction(name, callback, description, typeName, validator), defaultValue);
436 }
437
438 CLI::Option* SubCommand::setConfigurable(CLI::Option* option, bool configurable) const {
439 return option //
440 ->configurable(configurable)
441 ->group(subCommandApp->get_formatter()->get_label(configurable ? "Persistent Options" : "Nonpersistent Options"));
442 }
443
444 CLI::Option*
445 SubCommand::initialize(CLI::Option* option, const std::string& typeName, const CLI::Validator& validator, bool configurable) const {
446 return setConfigurable(option, configurable) //
447 ->type_name(typeName)
448 ->check(validator);
449 }
450
451 AppWithPtr::AppWithPtr(const std::string& description, const std::string& name, SubCommand* t, bool manage)
452 : CLI::App(description, name)
453 , ptr(t)
454 , manage(manage) {
455 }
456
458 if (manage) {
459 delete ptr;
460 }
461 }
462
463 const SubCommand* AppWithPtr::getPtr() const {
464 return ptr;
465 }
466
468 return ptr;
469 }
470
471 std::map<std::string, std::string> SubCommand::aliases;
472
473 CLI::App* SubCommand::helpTriggerApp = nullptr;
474 CLI::App* SubCommand::showConfigTriggerApp = nullptr;
475 CLI::App* SubCommand::commandlineTriggerApp = nullptr;
476
477} // namespace utils
const SubCommand * getPtr() const
SubCommand * getPtr()
~AppWithPtr() override
AppWithPtr(const std::string &description, const std::string &name, SubCommand *t, bool manage)
SubCommand * ptr
Definition SubCommand.h:92
std::string version() const
SubCommand * description(const std::string &description)
SubCommand * required(SubCommand *subCommand, bool required=true)
SubCommand * allowExtras(bool allow=true)
SubCommand * required(bool required=true, bool force=true)
CLI::Option * addOptionFunction(const std::string &name, std::function< void(const std::string &)> &callback, const std::string &description, const std::string &typeName, const CLI::Validator &validator) const
SubCommand * footer(const std::string &footer)
SubCommand * disabled(SubCommand *subCommand, bool disabled=true)
SubCommand(SubCommand *parent, std::shared_ptr< utils::AppWithPtr > appWithPtr, const std::string &group, bool final)
static CLI::App * showConfigTriggerApp
Definition SubCommand.h:238
SubCommand * getParent()
CLI::Option * addOption(const std::string &name, const std::string &description, const std::string &typeName, ValueTypeT defaultValue, const CLI::Validator &validator) const
Definition SubCommand.h:289
SubCommand * required(CLI::Option *option, bool required=true)
bool hasParent() const
CLI::Option * addFlagFunction(const std::string &name, const std::function< void()> &callback, const std::string &description, const std::string &typeName, const CLI::Validator &validator)
CLI::Option * setDefaultValue(CLI::Option *option, const ValueTypeT &value, bool clear=true) const
Definition SubCommand.h:337
CLI::Option * setVersionFlag(const std::string &version) const
CLI::Option * addFlagFunction(const std::string &name, const std::function< void()> &callback, const std::string &description, const std::string &typeName, const std::string &defaultValue, const CLI::Validator &validator)
CLI::Option * commandlineOpt
Definition SubCommand.h:251
CLI::Option * helpOpt
Definition SubCommand.h:249
SubCommand * finalCallback(const std::function< void()> &finalCallback)
AppWithPtr * subCommandApp
Definition SubCommand.h:244
virtual ~SubCommand()
static std::shared_ptr< CLI::Formatter > sectionFormatter
Definition SubCommand.h:232
SubCommand * parent
Definition SubCommand.h:245
static CLI::App * helpTriggerApp
Definition SubCommand.h:237
CLI::Option * addOption(const std::string &name, const std::string &description, const std::string &typeName, const CLI::Validator &validator) const
CLI::Option * getOption(const std::string &name) const
static CLI::App * getHelpTriggerApp()
static CLI::App * getCommandlineTriggerApp()
CLI::Option * setConfig(const std::string &defaultConfigFile) const
CLI::Option * showConfigOpt
Definition SubCommand.h:250
static std::map< std::string, std::string > aliases
Definition SubCommand.h:235
std::string help(const CLI::App *helpApp, const CLI::AppFormatMode &mode) const
SubCommand * needs(SubCommand *subCommand, bool needs=true)
void parse(int argc, char *argv[])
std::string configToStr() const
static CLI::App * commandlineTriggerApp
Definition SubCommand.h:239
CLI::Option * setLogFile(const std::string &defaultLogFile) const
static CLI::App * getShowConfigTriggerApp()
CLI::Option * setConfigurable(CLI::Option *option, bool configurable) const
std::string getName() const
CLI::Option * addFlag(const std::string &name, const std::string &description, const std::string &typeName, const CLI::Validator &validator) const
CLI::Option * initialize(CLI::Option *option, const std::string &typeName, const CLI::Validator &validator, bool configurable) const
SubCommand * setRequireCallback(const std::function< void(void)> &callback)
static std::shared_ptr< CLI::HelpFormatter > makeSectionFormatter()