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 , name(subCommandApp != nullptr ? subCommandApp->get_name() : "<NOAPP>")
60 , parent(parent)
61 , final(final) {
62 if (appWithPtr != nullptr) {
63 if (parent != nullptr) {
64 parent->addSubCommandApp(appWithPtr);
65 } else {
66 selfAnchoredSubCommandApp = std::move(appWithPtr);
67 }
68
69 subCommandApp->group(group);
70 subCommandApp->configurable(false);
71 subCommandApp->fallthrough();
72
73 static const std::shared_ptr<CLI::HelpFormatter> helpFormatter = std::make_shared<CLI::HelpFormatter>();
74 subCommandApp->formatter(helpFormatter);
75
76 static const std::shared_ptr<CLI::Config> configFormatter = std::make_shared<CLI::ConfigFormatter>();
77 subCommandApp->config_formatter(configFormatter);
78
79 subCommandApp->option_defaults()->take_last()->group(subCommandApp->get_formatter()->get_label("Nonpersistent Options"));
80
82 ->set_help_flag(
83 "--help{exact},-h{exact}",
84 [subCommandApp = this->subCommandApp](std::size_t) {
85 helpTriggerApp = subCommandApp;
86 },
87 "Print help message and exit\n"
88 "* standard: display help for the last command processed\n"
89 "* exact: display help for the command directly preceding --help")
90 ->take_first()
91 ->check(CLI::IsMember({"standard", "exact"}))
92 ->trigger_on_parse(),
93 false);
94
96 ->add_flag_function(
97 "-s,--show-config",
98 [subCommandApp = this->subCommandApp](std::size_t) {
99 showConfigTriggerApp = subCommandApp;
100 },
101 "Show current configuration and exit")
102 ->take_first()
103 ->disable_flag_override()
104 ->configurable(false)
105 ->trigger_on_parse(),
106 false);
107
109 ->add_flag_function(
110 "--command-line{standard}",
111 [subCommandApp = this->subCommandApp]([[maybe_unused]] std::int64_t count) {
112 commandlineTriggerApp = subCommandApp;
113 },
114 "Print command-line\n"
115 "* standard (default): Show all non-default and required options\n"
116 "* active: Show all active options\n"
117 "* complete: Show the complete option set with default values\n"
118 "* required: Show only required options")
119 ->take_first()
120 ->check(CLI::IsMember({"standard", "active", "complete", "required"}))
121 ->trigger_on_parse(),
122 false);
123 }
124 }
125
128
129 std::string SubCommand::getName() const {
130 return name;
131 }
132
133 std::string SubCommand::version() const {
134 return subCommandApp->version();
135 }
136
137 void SubCommand::parse(int argc, char* argv[]) const {
138 subCommandApp->parse(argc, argv);
139 }
140
141 SubCommand* SubCommand::description(const std::string& description) {
142 subCommandApp->description(description);
143
144 return this;
145 }
146
147 SubCommand* SubCommand::footer(const std::string& footer) {
148 subCommandApp->footer(footer);
149
150 return this;
151 }
152
154 for (const SubCommand* child : childSubCommands) {
155 delete child;
156 }
157 childSubCommands.clear();
158
159 if (parent != nullptr) {
160 parent->subCommandApp->remove_subcommand(subCommandApp);
161 }
162 }
163
164 CLI::Option* SubCommand::setConfig(const std::string& defaultConfigFile) const {
165 return subCommandApp
166 ->set_config( //
167 "-c,--config-file",
168 defaultConfigFile,
169 "Read a config file",
170 false) //
171 ->take_all()
172 ->type_name("configfile")
173 ->check(!CLI::ExistingDirectory);
174 }
175
176 CLI::Option* utils::SubCommand::setLogFile(const std::string& defaultLogFile) const {
177 return addOption("-l,--log-file", "Log file", "logFile", defaultLogFile, !CLI::ExistingDirectory);
178 }
179
180 CLI::Option* SubCommand::setVersionFlag(const std::string& version) const {
181 return subCommandApp->set_version_flag("-v,--version", version, "Framework version");
182 }
183
184 bool SubCommand::hasParent() const {
185 return subCommandApp->get_parent() != nullptr;
186 }
187
189 return parent;
190 }
191
192 SubCommand* SubCommand::setRequireCallback(const std::function<void()>& callback) {
193 subCommandApp->require_callback(callback);
194
195 return this;
196 }
197
199 subCommandApp->allow_extras(allow);
200
201 return this;
202 }
203
207
211
213 return helpTriggerApp;
214 }
215
216 SubCommand* SubCommand::required(bool required, bool force) {
217 requiredCount += required ? 1 : -1;
218
219 required = force ? required : requiredCount > 0;
220
221 subCommandApp->required(required);
222
223 SubCommand* parent = getParent();
224 if (parent != nullptr) {
225 if (required) {
226 parent->needs(this);
227 }
228
229 parent->required(this, required);
230 }
231
232 return this;
233 }
234
235 SubCommand* SubCommand::required(SubCommand* subCommand, bool required) {
236 if (subCommandApp->get_required() != required) {
237 if (required) {
238 needs(subCommand);
239
240 for (const auto& sub : subCommand->subCommandApp->get_subcommands([](const CLI::App* sc) -> bool {
241 return sc->get_required();
242 })) {
243 subCommand->subCommandApp->needs(sub);
244 }
245 } else {
246 needs(subCommand, false);
247
248 for (const auto& sub : subCommand->subCommandApp->get_subcommands([](const CLI::App* sc) -> bool {
249 return sc->get_required();
250 })) {
251 subCommand->subCommandApp->remove_needs(sub);
252 }
253 }
254
255 this->required(required, false);
256
257 subCommand->subCommandApp->required(required);
258 subCommand->subCommandApp->ignore_case(required);
259 }
260
261 return this;
262 }
263
264 SubCommand* SubCommand::required(CLI::Option* option, bool required) {
265 if (option->get_required() != required) {
266 option->required(required);
267
268 if (required) {
269 subCommandApp->needs(option);
270 option->default_str("");
271 } else {
272 subCommandApp->remove_needs(option);
273 }
274
275 this->required(required, false);
276 }
277
278 return this;
279 }
280
281 SubCommand* SubCommand::needs(SubCommand* subCommand, bool needs) {
282 if (needs) {
283 subCommandApp->needs(subCommand->subCommandApp);
284 } else {
285 subCommandApp->remove_needs(subCommand->subCommandApp);
286 }
287
288 return this;
289 }
290
291 SubCommand* SubCommand::disabled(SubCommand* subCommand, bool disabled) {
292 if (disabled) {
293 if (subCommand->subCommandApp->get_ignore_case()) {
294 needs(subCommand, false);
295 }
296
297 for (const auto& sub : subCommand->subCommandApp->get_subcommands({})) {
298 if (sub->get_ignore_case()) {
299 subCommand->subCommandApp->remove_needs(sub);
300 sub->required(false);
301 }
302 }
303 } else {
304 if (subCommand->subCommandApp->get_ignore_case()) {
305 needs(subCommand);
306 }
307
308 for (const auto& sub : subCommand->subCommandApp->get_subcommands({})) {
309 if (sub->get_ignore_case()) {
310 subCommand->subCommandApp->needs(sub);
311 sub->required();
312 }
313 }
314 }
315
316 subCommand->subCommandApp->required(disabled ? false : subCommand->subCommandApp->get_ignore_case());
317
318 return this;
319 }
320
321 SubCommand* SubCommand::finalCallback(const std::function<void()>& finalCallback) {
322 subCommandApp->final_callback(finalCallback);
323
324 return this;
325 }
326
327 std::string SubCommand::configToStr() const {
328 return subCommandApp->config_to_str(true, true);
329 }
330
331 std::string SubCommand::help(const CLI::App* helpApp, const CLI::AppFormatMode& mode) const {
332 return subCommandApp->help(helpApp, "", mode);
333 }
334
335 void SubCommand::addSubCommandApp(std::shared_ptr<AppWithPtr> subCommand) {
336 if (subCommandApp->get_subcommands({}).empty()) {
337 helpOpt->description("Print help message and exit\n"
338 "* standard: display help for the last command processed\n"
339 "* exact: display help for the command directly preceding --help\n"
340 "* expanded: display help including all descendant sections");
341 if (auto* existing = helpOpt->get_validator(); existing != nullptr) {
342 *existing = std::move(CLI::IsMember({"standard", "exact", "expanded"})); // replace, do not append
343 }
344 }
345
346 subCommandApp->add_subcommand(subCommand);
347 }
348
349 static std::shared_ptr<CLI::HelpFormatter> makeSectionFormatter() {
350 const std::shared_ptr<CLI::HelpFormatter> sectionFormatter = std::make_shared<CLI::HelpFormatter>();
351
352 sectionFormatter->label("SUBCOMMAND", "SECTION");
353 sectionFormatter->label("SUBCOMMANDS", "SECTIONS");
354 sectionFormatter->label("PERSISTENT", "");
355 sectionFormatter->label("Persistent Options", "Options (persistent)");
356 sectionFormatter->label("Nonpersistent Options", "Options (nonpersistent)");
357 sectionFormatter->label("Usage", "\nUsage");
358 sectionFormatter->label("bool:{true,false}", "{true,false}");
359 sectionFormatter->label(":{standard,active,complete,required}", "{standard,active,complete,required}");
360 sectionFormatter->label(":{standard,exact,expanded}", "{standard,exact,expanded}");
361 sectionFormatter->column_width(7);
362
363 return sectionFormatter;
364 }
365
366 std::shared_ptr<CLI::Formatter> SubCommand::sectionFormatter = makeSectionFormatter();
367
368 CLI::Option* SubCommand::getOption(const std::string& name) const {
369 return subCommandApp->get_option_no_throw(name);
370 }
371
372 CLI::Option* SubCommand::addOption(const std::string& name,
373 const std::string& description,
374 const std::string& typeName,
375 const CLI::Validator& validator) const {
377 ->add_option(name, description),
378 typeName,
379 validator,
380 !subCommandApp->get_disabled());
381 }
382
383 CLI::Option* SubCommand::addOptionFunction(const std::string& name,
384 const std::function<void(const std::string&)>& callback,
385 const std::string& description,
386 const std::string& typeName,
387 const CLI::Validator& validator) const {
389 ->add_option_function(name, callback, description),
390 typeName,
391 validator,
392 !subCommandApp->get_disabled());
393 }
394
395 CLI::Option* SubCommand::addFlag(const std::string& name,
396 const std::string& description,
397 const std::string& typeName,
398 const CLI::Validator& validator) const {
400 ->add_flag(name, description),
401 typeName,
402 validator,
403 !subCommandApp->get_disabled());
404 }
405
406 CLI::Option* SubCommand::addFlagFunction(const std::string& name,
407 const std::function<void()>& callback,
408 const std::string& description,
409 const std::string& typeName,
410 const CLI::Validator& validator) const {
411 CLI::Option* opt = subCommandApp //
412 ->add_flag_function(
413 name,
414 [callback](std::int64_t) {
415 callback();
416 },
417 description)
418 ->type_name(typeName)
419 ->check(validator)
420 ->take_last();
421 if (opt->get_configurable()) {
422 opt->group(subCommandApp->get_formatter()->get_label("Persistent Options"));
423 }
424
425 return opt;
426 }
427
428 CLI::Option* SubCommand::addFlagFunction(const std::string& name,
429 const std::function<void()>& callback,
430 const std::string& description,
431 const std::string& typeName,
432 const std::string& defaultValue,
433 const CLI::Validator& validator) const {
434 return setDefaultValue(addFlagFunction(name, callback, description, typeName, validator), defaultValue);
435 }
436
437 CLI::Option* SubCommand::setConfigurable(CLI::Option* option, bool configurable) const {
438 return option //
439 ->configurable(configurable)
440 ->group(subCommandApp->get_formatter()->get_label(configurable ? "Persistent Options" : "Nonpersistent Options"));
441 }
442
443 CLI::Option*
444 SubCommand::initialize(CLI::Option* option, const std::string& typeName, const CLI::Validator& validator, bool configurable) const {
445 return setConfigurable(option, configurable) //
446 ->type_name(typeName)
447 ->check(validator);
448 }
449
450 AppWithPtr::AppWithPtr(const std::string& description, const std::string& name, SubCommand* t)
451 : CLI::App(description, name)
452 , ptr(t) {
453 }
454
457
458 const SubCommand* AppWithPtr::getPtr() const {
459 return ptr;
460 }
461
463 return ptr;
464 }
465
466 std::map<std::string, std::string> SubCommand::aliases;
467
468 CLI::App* SubCommand::helpTriggerApp = nullptr;
469 CLI::App* SubCommand::showConfigTriggerApp = nullptr;
470 CLI::App* SubCommand::commandlineTriggerApp = nullptr;
471
472} // namespace utils
const SubCommand * getPtr() const
SubCommand * getPtr()
~AppWithPtr() override
AppWithPtr(const std::string &description, const std::string &name, SubCommand *t)
SubCommand * ptr
Definition SubCommand.h:94
CLI::Option * addFlagFunction(const std::string &name, const std::function< void()> &callback, const std::string &description, const std::string &typeName, const CLI::Validator &validator) const
std::string version() const
SubCommand * description(const std::string &description)
std::shared_ptr< AppWithPtr > selfAnchoredSubCommandApp
Definition SubCommand.h:255
SubCommand * required(SubCommand *subCommand, bool required=true)
SubCommand * allowExtras(bool allow=true)
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) const
SubCommand * required(bool required=true, bool force=true)
SubCommand * footer(const std::string &footer)
SubCommand * disabled(SubCommand *subCommand, bool disabled=true)
void parse(int argc, char *argv[]) const
static CLI::App * showConfigTriggerApp
Definition SubCommand.h:245
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:319
SubCommand * required(CLI::Option *option, bool required=true)
bool hasParent() const
SubCommand(SubCommand *parent, std::shared_ptr< utils::AppWithPtr > appWithPtr, const std::string &group, bool final=false)
CLI::Option * setDefaultValue(CLI::Option *option, const ValueTypeT &value, bool clear=true) const
Definition SubCommand.h:367
std::set< SubCommand * > childSubCommands
Definition SubCommand.h:256
CLI::Option * setVersionFlag(const std::string &version) const
CLI::Option * commandlineOpt
Definition SubCommand.h:265
CLI::Option * helpOpt
Definition SubCommand.h:261
SubCommand * finalCallback(const std::function< void()> &finalCallback)
AppWithPtr * subCommandApp
Definition SubCommand.h:251
virtual ~SubCommand()
static std::shared_ptr< CLI::Formatter > sectionFormatter
Definition SubCommand.h:240
SubCommand * parent
Definition SubCommand.h:253
static CLI::App * helpTriggerApp
Definition SubCommand.h:244
std::string name
Definition SubCommand.h:252
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:264
static std::map< std::string, std::string > aliases
Definition SubCommand.h:242
std::string help(const CLI::App *helpApp, const CLI::AppFormatMode &mode) const
SubCommand * needs(SubCommand *subCommand, bool needs=true)
std::string configToStr() const
static CLI::App * commandlineTriggerApp
Definition SubCommand.h:246
CLI::Option * setLogFile(const std::string &defaultLogFile) const
void addSubCommandApp(std::shared_ptr< utils::AppWithPtr > subCommand)
CLI::Option * addOptionFunction(const std::string &name, const std::function< void(const std::string &)> &callback, const std::string &description, const std::string &typeName, const CLI::Validator &validator) const
SubCommand * getParent() 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()