1//! @file command.cpp
2//! @author ryftchen
3//! @brief The definitions (command) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#include "command.hpp"
8#include "log.hpp"
9#include "view.hpp"
10
11#ifndef _PRECOMPILED_HEADER
12#include <barrier>
13#include <latch>
14#include <numeric>
15#include <ranges>
16#else
17#include "application/pch/precompiled_header.hpp"
18#endif // _PRECOMPILED_HEADER
19
20#include "utility/include/benchmark.hpp"
21#include "utility/include/currying.hpp"
22#include "utility/include/time.hpp"
23
24namespace application::command
25{
26//! @brief Anonymous namespace.
27inline namespace
28{
29//! @brief The semaphore that controls the maximum access limit.
30std::counting_semaphore<1> cliSem(1);
31} // namespace
32
33//! @brief Manage external helpers.
34namespace help
35{
36//! @brief Constraint for external helpers.
37//! @tparam Type - type of helper
38template <typename Type>
39concept ExtHelper =
40 !std::is_constructible_v<Type> && !std::is_copy_constructible_v<Type> && !std::is_copy_assignable_v<Type>
41 && !std::is_move_constructible_v<Type> && !std::is_move_assignable_v<Type> && requires (const Type& /*helper*/) {
42 { Type::getInstance() } -> std::same_as<std::shared_ptr<Type>>;
43 };
44
45//! @brief Enumerate specific events to control external helpers.
46enum class ExtEvent : std::uint8_t
47{
48 //! @brief Startup.
49 startup,
50 //! @brief Shutdown.
51 shutdown,
52 //! @brief Reload.
53 reload
54};
55
56//! @brief Trigger the external helper with event.
57//! @tparam Helper - type of helper
58//! @param event - target event
59template <ExtHelper Helper>
60requires std::derived_from<Helper, utility::fsm::FSM<Helper>>
61static void triggerEvent(const ExtEvent event)
62{
63 if (!configure::detail::activateHelper())
64 {
65 return;
66 }
67
68 switch (event)
69 {
70 case ExtEvent::startup:
71 typename Helper::Access().startup();
72 return;
73 case ExtEvent::shutdown:
74 typename Helper::Access().shutdown();
75 return;
76 case ExtEvent::reload:
77 typename Helper::Access().reload();
78 return;
79 default:
80 break;
81 }
82}
83
84//! @brief Helper daemon service.
85//! @tparam Helpers - type of arguments of helper
86template <ExtHelper... Helpers>
87requires (std::derived_from<Helpers, utility::fsm::FSM<Helpers>> && ...)
88static void daemonService()
89{
90 utility::thread::Thread extendedJob(sizeof...(Helpers));
91 (extendedJob.enqueue(Helpers::name, &Helpers::service, Helpers::getInstance()), ...);
92}
93
94//! @brief Coroutine for managing the lifecycle of helper components.
95//! @tparam Hs - type of helpers
96//! @return awaitable instance
97template <typename... Hs>
98static action::Awaitable launchLifecycle()
99{
100 if (!configure::detail::activateHelper())
101 {
102 co_return;
103 }
104
105 std::latch waitPoint(1);
106 const std::jthread daemon(
107 [&waitPoint]()
108 {
109 daemonService<Hs...>();
110 waitPoint.count_down();
111 });
112 std::barrier syncPoint(sizeof...(Hs) + 1);
113 static constexpr auto publish = [](std::barrier<>& phase, const ExtEvent event) constexpr
114 {
115 std::vector<std::jthread> senders{};
116 senders.reserve(n: sizeof...(Hs));
117 (senders.emplace_back(args: std::jthread{[&phase, event]()
118 {
119 triggerEvent<Hs>(event);
120 phase.arrive_and_wait();
121 }}),
122 ...);
123 phase.arrive_and_wait();
124 };
125
126 co_await std::suspend_always{};
127 publish(syncPoint, ExtEvent::startup);
128 co_await std::suspend_always{};
129 publish(syncPoint, ExtEvent::shutdown);
130
131 waitPoint.wait();
132}
133
134//! @brief Enter the next phase of the coroutine of helpers.
135//! @param awaitable - awaitable instance
136static void enterNextPhase(action::Awaitable& awaitable)
137{
138 if (!awaitable.done())
139 {
140 awaitable.resume();
141 }
142}
143} // namespace help
144
145//! @brief Convert category enumeration to string.
146//! @param cat - native category
147//! @return category name
148static constexpr std::string_view toString(const Category cat)
149{
150 constexpr std::array<std::string_view, Bottom<Category>::value> stringify = {
151 MACRO_STRINGIFY(console), MACRO_STRINGIFY(dump), MACRO_STRINGIFY(help), MACRO_STRINGIFY(version)};
152 return stringify.at(n: static_cast<std::uint8_t>(cat));
153}
154
155//! @brief Get the Command instance.
156//! @return reference of the Command object
157Command& getInstance()
158{
159 static Command commander{};
160 return commander;
161}
162
163template <typename Key, typename Subject>
164void Notifier<Key, Subject>::attach(const Key key, std::shared_ptr<ProcBase> handler)
165{
166 handlers[key] = std::move(handler);
167}
168
169template <typename Key, typename Subject>
170void Notifier<Key, Subject>::notify(const Key key) const
171{
172 if (handlers.contains(key))
173 {
174 handlers.at(key)->execute();
175 }
176}
177
178// clang-format off
179//! @brief Mapping table for enum and attribute about command categories. X macro.
180#define COMMAND_CATEGORY_X_MACRO_MAPPING \
181 X(Category::console, "c", "run options in console mode and exit\n" \
182 "separate with quotes" ) \
183 X(Category::dump , "d", "dump default configuration and exit" ) \
184 X(Category::help , "h", "show this help message and exit" ) \
185 X(Category::version, "v", "show version information and exit" )
186// clang-format on
187consteval std::string_view Command::mappedAlias(const Category cat)
188{
189//! @cond
190#define X(enum, descr, alias) {descr, alias},
191 constexpr std::string_view table[][2] = {COMMAND_CATEGORY_X_MACRO_MAPPING};
192 static_assert((sizeof(table) / sizeof(table[0])) == Bottom<Category>::value);
193 return table[static_cast<std::uint8_t>(cat)][0];
194//! @endcond
195#undef X
196}
197
198consteval std::string_view Command::mappedDescr(const Category cat)
199{
200//! @cond
201#define X(enum, descr, alias) {descr, alias},
202 constexpr std::string_view table[][2] = {COMMAND_CATEGORY_X_MACRO_MAPPING};
203 static_assert((sizeof(table) / sizeof(table[0])) == Bottom<Category>::value);
204 return table[static_cast<std::uint8_t>(cat)][1];
205//! @endcond
206#undef X
207}
208#undef COMMAND_CATEGORY_X_MACRO_MAPPING
209
210Command::Command()
211{
212 initializeNativeCLI();
213 initializeExtraCLI();
214}
215
216Command::~Command()
217{
218 clearSelected();
219}
220
221bool Command::execute(const int argc, const char* const argv[])
222try
223{
224 isFaulty.store(i: false);
225 isParsed.store(i: false);
226 auto helpCtrl = help::launchLifecycle<log::Log, view::View>();
227 help::enterNextPhase(awaitable&: helpCtrl);
228
229 if (argc > 1)
230 {
231 constexpr std::uint8_t endNum = 2;
232 utility::thread::Thread scheduledJob(endNum);
233 scheduledJob.enqueue(name: title + "-front", func: &Command::frontEndHandler, args: this, args: argc, args&: argv);
234 scheduledJob.enqueue(name: title + "-back", func: &Command::backEndHandler, args: this);
235 }
236 else
237 {
238 enterConsoleMode();
239 }
240
241 help::enterNextPhase(awaitable&: helpCtrl);
242 return !isFaulty.load();
243}
244catch (const std::exception& err)
245{
246 isFaulty.store(i: true);
247 LOG_ERR << err.what();
248 return !isFaulty.load();
249}
250
251void Command::setupMainCLI()
252{
253 using ArgsNumPattern = utility::argument::ArgsNumPattern;
254 mainCLI
255 .addArgument(
256 fewArgs: shortPrefix + std::string{mappedAlias(cat: Category::help)}, fewArgs: longPrefix + std::string{toString(cat: Category::help)})
257 .argsNum(num: 0)
258 .implicitValue(value: true)
259 .help(message: mappedDescr(cat: Category::help));
260 builtInNotifier.attach(key: Category::help, handler: std::make_shared<LocalNotifier::Handler<Category::help>>(args&: *this));
261 mainCLI
262 .addArgument(
263 fewArgs: shortPrefix + std::string{mappedAlias(cat: Category::version)},
264 fewArgs: longPrefix + std::string{toString(cat: Category::version)})
265 .argsNum(num: 0)
266 .implicitValue(value: true)
267 .help(message: mappedDescr(cat: Category::version));
268 builtInNotifier.attach(key: Category::version, handler: std::make_shared<LocalNotifier::Handler<Category::version>>(args&: *this));
269 mainCLI
270 .addArgument(
271 fewArgs: shortPrefix + std::string{mappedAlias(cat: Category::dump)}, fewArgs: longPrefix + std::string{toString(cat: Category::dump)})
272 .argsNum(num: 0)
273 .implicitValue(value: true)
274 .help(message: mappedDescr(cat: Category::dump));
275 builtInNotifier.attach(key: Category::dump, handler: std::make_shared<LocalNotifier::Handler<Category::dump>>(args&: *this));
276 mainCLI
277 .addArgument(
278 fewArgs: shortPrefix + std::string{mappedAlias(cat: Category::console)},
279 fewArgs: longPrefix + std::string{toString(cat: Category::console)})
280 .argsNum(pattern: ArgsNumPattern::any)
281 .defaultValue<std::vector<std::string>>(value: {"usage"})
282 .appending()
283 .action(
284 callable: [](const std::string& input)
285 {
286 if (std::all_of(
287 first: input.cbegin(), last: input.cend(), pred: [l = std::locale{}](const auto c) { return std::isspace(c, l); }))
288 {
289 throw std::runtime_error{"Invalid " + std::string{toString(cat: Category::console)} + " command."};
290 }
291 return input;
292 })
293 .metaVariable(variable: "CMD")
294 .help(message: mappedDescr(cat: Category::console));
295 builtInNotifier.attach(key: Category::console, handler: std::make_shared<LocalNotifier::Handler<Category::console>>(args&: *this));
296}
297
298// NOLINTBEGIN(google-build-using-namespace, readability-function-size)
299//! @brief Setup the sub-command line interface (algorithm module).
300template <>
301void Command::setupSubCLI<reg_algo::ApplyAlgorithm>()
302{
303 using namespace reg_algo;
304 using Intf = ExtraManager::Intf;
305 using Attr = ExtraManager::Attr;
306 using action::name, action::alias, action::descr;
307 constexpr std::string_view helpDescr = mappedDescr(cat: Category::help);
308 const std::string helpArg1 = shortPrefix + std::string{mappedAlias(cat: Category::help)};
309 const std::string helpArg2 = longPrefix + std::string{toString(cat: Category::help)};
310 auto& registry = taskDispatcher.extraChoiceRegistry[subCLIAppAlgo.title()];
311 std::vector<std::string> candidates{};
312
313 taskDispatcher.extraChecklist.emplace(args: subCLIAppAlgo.title(), args: Intf{manage::present, manage::clear});
314 subCLIAppAlgo.addDescription(text: descr<ApplyAlgorithm>());
315 subCLIAppAlgo.addArgument(fewArgs: helpArg1, fewArgs: helpArg2).argsNum(num: 0).implicitValue(value: true).help(message: helpDescr);
316
317 candidates = extractChoices<MatchMethod>();
318 registry.emplace(args: name<MatchMethod>(), args: Attr{.choices: candidates, .event: MatchMethod{}});
319 subCLIAppAlgo
320 .addArgument(fewArgs: shortPrefix + std::string{alias<MatchMethod>()}, fewArgs: longPrefix + std::string{name<MatchMethod>()})
321 .argsNum(numMin: 0, numMax: candidates.size())
322 .defaultValue<decltype(candidates)>(value: std::move(candidates))
323 .remaining()
324 .metaVariable(variable: metaVar)
325 .help(message: descr<MatchMethod>());
326 applyingForwarder.registerHandler(handling: [](const action::SetChoice<MatchMethod>& msg)
327 { setChoice<MatchMethod>(msg.choice); });
328 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<MatchMethod>& msg)
329 { runCandidates<MatchMethod>(candidates: msg.candidates); });
330 versionLinks.emplace(args: VerLinkKey{name<ApplyAlgorithm>(), match::version()}, args: name<MatchMethod>());
331 candidates = extractChoices<NotationMethod>();
332 registry.emplace(args: name<NotationMethod>(), args: Attr{.choices: candidates, .event: NotationMethod{}});
333 subCLIAppAlgo
334 .addArgument(
335 fewArgs: shortPrefix + std::string{alias<NotationMethod>()}, fewArgs: longPrefix + std::string{name<NotationMethod>()})
336 .argsNum(numMin: 0, numMax: candidates.size())
337 .defaultValue<decltype(candidates)>(value: std::move(candidates))
338 .remaining()
339 .metaVariable(variable: metaVar)
340 .help(message: descr<NotationMethod>());
341 applyingForwarder.registerHandler(handling: [](const action::SetChoice<NotationMethod>& msg)
342 { setChoice<NotationMethod>(msg.choice); });
343 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<NotationMethod>& msg)
344 { runCandidates<NotationMethod>(candidates: msg.candidates); });
345 versionLinks.emplace(args: VerLinkKey{name<ApplyAlgorithm>(), notation::version()}, args: name<NotationMethod>());
346 candidates = extractChoices<OptimalMethod>();
347 registry.emplace(args: name<OptimalMethod>(), args: Attr{.choices: candidates, .event: OptimalMethod{}});
348 subCLIAppAlgo
349 .addArgument(fewArgs: shortPrefix + std::string{alias<OptimalMethod>()}, fewArgs: longPrefix + std::string{name<OptimalMethod>()})
350 .argsNum(numMin: 0, numMax: candidates.size())
351 .defaultValue<decltype(candidates)>(value: std::move(candidates))
352 .remaining()
353 .metaVariable(variable: metaVar)
354 .help(message: descr<OptimalMethod>());
355 applyingForwarder.registerHandler(handling: [](const action::SetChoice<OptimalMethod>& msg)
356 { setChoice<OptimalMethod>(msg.choice); });
357 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<OptimalMethod>& msg)
358 { runCandidates<OptimalMethod>(candidates: msg.candidates); });
359 versionLinks.emplace(args: VerLinkKey{name<ApplyAlgorithm>(), optimal::version()}, args: name<OptimalMethod>());
360 candidates = extractChoices<SearchMethod>();
361 registry.emplace(args: name<SearchMethod>(), args: Attr{.choices: candidates, .event: SearchMethod{}});
362 subCLIAppAlgo
363 .addArgument(fewArgs: shortPrefix + std::string{alias<SearchMethod>()}, fewArgs: longPrefix + std::string{name<SearchMethod>()})
364 .argsNum(numMin: 0, numMax: candidates.size())
365 .defaultValue<decltype(candidates)>(value: std::move(candidates))
366 .remaining()
367 .metaVariable(variable: metaVar)
368 .help(message: descr<SearchMethod>());
369 applyingForwarder.registerHandler(handling: [](const action::SetChoice<SearchMethod>& msg)
370 { setChoice<SearchMethod>(msg.choice); });
371 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<SearchMethod>& msg)
372 { runCandidates<SearchMethod>(candidates: msg.candidates); });
373 versionLinks.emplace(args: VerLinkKey{name<ApplyAlgorithm>(), search::version()}, args: name<SearchMethod>());
374 candidates = extractChoices<SortMethod>();
375 registry.emplace(args: name<SortMethod>(), args: Attr{.choices: candidates, .event: SortMethod{}});
376 subCLIAppAlgo
377 .addArgument(fewArgs: shortPrefix + std::string{alias<SortMethod>()}, fewArgs: longPrefix + std::string{name<SortMethod>()})
378 .argsNum(numMin: 0, numMax: candidates.size())
379 .defaultValue<decltype(candidates)>(value: std::move(candidates))
380 .remaining()
381 .metaVariable(variable: metaVar)
382 .help(message: descr<SortMethod>());
383 applyingForwarder.registerHandler(handling: [](const action::SetChoice<SortMethod>& msg)
384 { setChoice<SortMethod>(msg.choice); });
385 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<SortMethod>& msg)
386 { runCandidates<SortMethod>(candidates: msg.candidates); });
387 versionLinks.emplace(args: VerLinkKey{name<ApplyAlgorithm>(), sort::version()}, args: name<SortMethod>());
388
389 mainCLI.addSubParser(parser&: subCLIAppAlgo);
390}
391
392//! @brief Setup the sub-command line interface (design pattern module).
393template <>
394void Command::setupSubCLI<reg_dp::ApplyDesignPattern>()
395{
396 using namespace reg_dp;
397 using Intf = ExtraManager::Intf;
398 using Attr = ExtraManager::Attr;
399 using action::name, action::alias, action::descr;
400 constexpr std::string_view helpDescr = mappedDescr(cat: Category::help);
401 const std::string helpArg1 = shortPrefix + std::string{mappedAlias(cat: Category::help)};
402 const std::string helpArg2 = longPrefix + std::string{toString(cat: Category::help)};
403 auto& registry = taskDispatcher.extraChoiceRegistry[subCLIAppDp.title()];
404 std::vector<std::string> candidates{};
405
406 taskDispatcher.extraChecklist.emplace(args: subCLIAppDp.title(), args: Intf{manage::present, manage::clear});
407 subCLIAppDp.addDescription(text: descr<ApplyDesignPattern>());
408 subCLIAppDp.addArgument(fewArgs: helpArg1, fewArgs: helpArg2).argsNum(num: 0).implicitValue(value: true).help(message: helpDescr);
409
410 candidates = extractChoices<BehavioralInstance>();
411 registry.emplace(args: name<BehavioralInstance>(), args: Attr{.choices: candidates, .event: BehavioralInstance{}});
412 subCLIAppDp
413 .addArgument(
414 fewArgs: shortPrefix + std::string{alias<BehavioralInstance>()},
415 fewArgs: longPrefix + std::string{name<BehavioralInstance>()})
416 .argsNum(numMin: 0, numMax: candidates.size())
417 .defaultValue<decltype(candidates)>(value: std::move(candidates))
418 .remaining()
419 .metaVariable(variable: metaVar)
420 .help(message: descr<BehavioralInstance>());
421 applyingForwarder.registerHandler(handling: [](const action::SetChoice<BehavioralInstance>& msg)
422 { setChoice<BehavioralInstance>(msg.choice); });
423 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<BehavioralInstance>& msg)
424 { runCandidates<BehavioralInstance>(candidates: msg.candidates); });
425 versionLinks.emplace(args: VerLinkKey{name<ApplyDesignPattern>(), behavioral::version()}, args: name<BehavioralInstance>());
426 candidates = extractChoices<CreationalInstance>();
427 registry.emplace(args: name<CreationalInstance>(), args: Attr{.choices: candidates, .event: CreationalInstance{}});
428 subCLIAppDp
429 .addArgument(
430 fewArgs: shortPrefix + std::string{alias<CreationalInstance>()},
431 fewArgs: longPrefix + std::string{name<CreationalInstance>()})
432 .argsNum(numMin: 0, numMax: candidates.size())
433 .defaultValue<decltype(candidates)>(value: std::move(candidates))
434 .remaining()
435 .metaVariable(variable: metaVar)
436 .help(message: descr<CreationalInstance>());
437 applyingForwarder.registerHandler(handling: [](const action::SetChoice<CreationalInstance>& msg)
438 { setChoice<CreationalInstance>(msg.choice); });
439 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<CreationalInstance>& msg)
440 { runCandidates<CreationalInstance>(candidates: msg.candidates); });
441 versionLinks.emplace(args: VerLinkKey{name<ApplyDesignPattern>(), creational::version()}, args: name<CreationalInstance>());
442 candidates = extractChoices<StructuralInstance>();
443 registry.emplace(args: name<StructuralInstance>(), args: Attr{.choices: candidates, .event: StructuralInstance{}});
444 subCLIAppDp
445 .addArgument(
446 fewArgs: shortPrefix + std::string{alias<StructuralInstance>()},
447 fewArgs: longPrefix + std::string{name<StructuralInstance>()})
448 .argsNum(numMin: 0, numMax: candidates.size())
449 .defaultValue<decltype(candidates)>(value: std::move(candidates))
450 .remaining()
451 .metaVariable(variable: metaVar)
452 .help(message: descr<StructuralInstance>());
453 applyingForwarder.registerHandler(handling: [](const action::SetChoice<StructuralInstance>& msg)
454 { setChoice<StructuralInstance>(msg.choice); });
455 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<StructuralInstance>& msg)
456 { runCandidates<StructuralInstance>(candidates: msg.candidates); });
457 versionLinks.emplace(args: VerLinkKey{name<ApplyDesignPattern>(), structural::version()}, args: name<StructuralInstance>());
458
459 mainCLI.addSubParser(parser&: subCLIAppDp);
460}
461
462//! @brief Setup the sub-command line interface (data structure module).
463template <>
464void Command::setupSubCLI<reg_ds::ApplyDataStructure>()
465{
466 using namespace reg_ds;
467 using Intf = ExtraManager::Intf;
468 using Attr = ExtraManager::Attr;
469 using action::name, action::alias, action::descr;
470 constexpr std::string_view helpDescr = mappedDescr(cat: Category::help);
471 const std::string helpArg1 = shortPrefix + std::string{mappedAlias(cat: Category::help)};
472 const std::string helpArg2 = longPrefix + std::string{toString(cat: Category::help)};
473 auto& registry = taskDispatcher.extraChoiceRegistry[subCLIAppDs.title()];
474 std::vector<std::string> candidates{};
475
476 taskDispatcher.extraChecklist.emplace(args: subCLIAppDs.title(), args: Intf{manage::present, manage::clear});
477 subCLIAppDs.addDescription(text: descr<ApplyDataStructure>());
478 subCLIAppDs.addArgument(fewArgs: helpArg1, fewArgs: helpArg2).argsNum(num: 0).implicitValue(value: true).help(message: helpDescr);
479
480 candidates = extractChoices<CacheInstance>();
481 registry.emplace(args: name<CacheInstance>(), args: Attr{.choices: candidates, .event: CacheInstance{}});
482 subCLIAppDs
483 .addArgument(fewArgs: shortPrefix + std::string{alias<CacheInstance>()}, fewArgs: longPrefix + std::string{name<CacheInstance>()})
484 .argsNum(numMin: 0, numMax: candidates.size())
485 .defaultValue<decltype(candidates)>(value: std::move(candidates))
486 .remaining()
487 .metaVariable(variable: metaVar)
488 .help(message: descr<CacheInstance>());
489 applyingForwarder.registerHandler(handling: [](const action::SetChoice<CacheInstance>& msg)
490 { setChoice<CacheInstance>(msg.choice); });
491 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<CacheInstance>& msg)
492 { runCandidates<CacheInstance>(candidates: msg.candidates); });
493 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), cache::version()}, args: name<CacheInstance>());
494 candidates = extractChoices<FilterInstance>();
495 registry.emplace(args: name<FilterInstance>(), args: Attr{.choices: candidates, .event: FilterInstance{}});
496 subCLIAppDs
497 .addArgument(
498 fewArgs: shortPrefix + std::string{alias<FilterInstance>()}, fewArgs: longPrefix + std::string{name<FilterInstance>()})
499 .argsNum(numMin: 0, numMax: candidates.size())
500 .defaultValue<decltype(candidates)>(value: std::move(candidates))
501 .remaining()
502 .metaVariable(variable: metaVar)
503 .help(message: descr<FilterInstance>());
504 applyingForwarder.registerHandler(handling: [](const action::SetChoice<FilterInstance>& msg)
505 { setChoice<FilterInstance>(msg.choice); });
506 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<FilterInstance>& msg)
507 { runCandidates<FilterInstance>(candidates: msg.candidates); });
508 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), filter::version()}, args: name<FilterInstance>());
509 candidates = extractChoices<GraphInstance>();
510 registry.emplace(args: name<GraphInstance>(), args: Attr{.choices: candidates, .event: GraphInstance{}});
511 subCLIAppDs
512 .addArgument(fewArgs: shortPrefix + std::string{alias<GraphInstance>()}, fewArgs: longPrefix + std::string{name<GraphInstance>()})
513 .argsNum(numMin: 0, numMax: candidates.size())
514 .defaultValue<decltype(candidates)>(value: std::move(candidates))
515 .remaining()
516 .metaVariable(variable: metaVar)
517 .help(message: descr<GraphInstance>());
518 applyingForwarder.registerHandler(handling: [](const action::SetChoice<GraphInstance>& msg)
519 { setChoice<GraphInstance>(msg.choice); });
520 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<GraphInstance>& msg)
521 { runCandidates<GraphInstance>(candidates: msg.candidates); });
522 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), graph::version()}, args: name<GraphInstance>());
523 candidates = extractChoices<HeapInstance>();
524 registry.emplace(args: name<HeapInstance>(), args: Attr{.choices: candidates, .event: HeapInstance{}});
525 subCLIAppDs
526 .addArgument(fewArgs: shortPrefix + std::string{alias<HeapInstance>()}, fewArgs: longPrefix + std::string{name<HeapInstance>()})
527 .argsNum(numMin: 0, numMax: candidates.size())
528 .defaultValue<decltype(candidates)>(value: std::move(candidates))
529 .remaining()
530 .metaVariable(variable: metaVar)
531 .help(message: descr<HeapInstance>());
532 applyingForwarder.registerHandler(handling: [](const action::SetChoice<HeapInstance>& msg)
533 { setChoice<HeapInstance>(msg.choice); });
534 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<HeapInstance>& msg)
535 { runCandidates<HeapInstance>(candidates: msg.candidates); });
536 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), heap::version()}, args: name<HeapInstance>());
537 candidates = extractChoices<LinearInstance>();
538 registry.emplace(args: name<LinearInstance>(), args: Attr{.choices: candidates, .event: LinearInstance{}});
539 subCLIAppDs
540 .addArgument(
541 fewArgs: shortPrefix + std::string{alias<LinearInstance>()}, fewArgs: longPrefix + std::string{name<LinearInstance>()})
542 .argsNum(numMin: 0, numMax: candidates.size())
543 .defaultValue<decltype(candidates)>(value: std::move(candidates))
544 .remaining()
545 .metaVariable(variable: metaVar)
546 .help(message: descr<LinearInstance>());
547 applyingForwarder.registerHandler(handling: [](const action::SetChoice<LinearInstance>& msg)
548 { setChoice<LinearInstance>(msg.choice); });
549 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<LinearInstance>& msg)
550 { runCandidates<LinearInstance>(candidates: msg.candidates); });
551 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), linear::version()}, args: name<LinearInstance>());
552 candidates = extractChoices<TreeInstance>();
553 registry.emplace(args: name<TreeInstance>(), args: Attr{.choices: candidates, .event: TreeInstance{}});
554 subCLIAppDs
555 .addArgument(fewArgs: shortPrefix + std::string{alias<TreeInstance>()}, fewArgs: longPrefix + std::string{name<TreeInstance>()})
556 .argsNum(numMin: 0, numMax: candidates.size())
557 .defaultValue<decltype(candidates)>(value: std::move(candidates))
558 .remaining()
559 .metaVariable(variable: metaVar)
560 .help(message: descr<TreeInstance>());
561 applyingForwarder.registerHandler(handling: [](const action::SetChoice<TreeInstance>& msg)
562 { setChoice<TreeInstance>(msg.choice); });
563 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<TreeInstance>& msg)
564 { runCandidates<TreeInstance>(candidates: msg.candidates); });
565 versionLinks.emplace(args: VerLinkKey{name<ApplyDataStructure>(), tree::version()}, args: name<TreeInstance>());
566
567 mainCLI.addSubParser(parser&: subCLIAppDs);
568}
569
570//! @brief Setup the sub-command line interface (numeric module).
571template <>
572void Command::setupSubCLI<reg_num::ApplyNumeric>()
573{
574 using namespace reg_num;
575 using Intf = ExtraManager::Intf;
576 using Attr = ExtraManager::Attr;
577 using action::name, action::alias, action::descr;
578 constexpr std::string_view helpDescr = mappedDescr(cat: Category::help);
579 const std::string helpArg1 = shortPrefix + std::string{mappedAlias(cat: Category::help)};
580 const std::string helpArg2 = longPrefix + std::string{toString(cat: Category::help)};
581 auto& registry = taskDispatcher.extraChoiceRegistry[subCLIAppNum.title()];
582 std::vector<std::string> candidates{};
583
584 taskDispatcher.extraChecklist.emplace(args: subCLIAppNum.title(), args: Intf{manage::present, manage::clear});
585 subCLIAppNum.addDescription(text: descr<ApplyNumeric>());
586 subCLIAppNum.addArgument(fewArgs: helpArg1, fewArgs: helpArg2).argsNum(num: 0).implicitValue(value: true).help(message: helpDescr);
587
588 candidates = extractChoices<ArithmeticMethod>();
589 registry.emplace(args: name<ArithmeticMethod>(), args: Attr{.choices: candidates, .event: ArithmeticMethod{}});
590 subCLIAppNum
591 .addArgument(
592 fewArgs: shortPrefix + std::string{alias<ArithmeticMethod>()}, fewArgs: longPrefix + std::string{name<ArithmeticMethod>()})
593 .argsNum(numMin: 0, numMax: candidates.size())
594 .defaultValue<decltype(candidates)>(value: std::move(candidates))
595 .remaining()
596 .metaVariable(variable: metaVar)
597 .help(message: descr<ArithmeticMethod>());
598 applyingForwarder.registerHandler(handling: [](const action::SetChoice<ArithmeticMethod>& msg)
599 { setChoice<ArithmeticMethod>(msg.choice); });
600 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<ArithmeticMethod>& msg)
601 { runCandidates<ArithmeticMethod>(candidates: msg.candidates); });
602 versionLinks.emplace(args: VerLinkKey{name<ApplyNumeric>(), arithmetic::version()}, args: name<ArithmeticMethod>());
603 candidates = extractChoices<DivisorMethod>();
604 registry.emplace(args: name<DivisorMethod>(), args: Attr{.choices: candidates, .event: DivisorMethod{}});
605 subCLIAppNum
606 .addArgument(fewArgs: shortPrefix + std::string{alias<DivisorMethod>()}, fewArgs: longPrefix + std::string{name<DivisorMethod>()})
607 .argsNum(numMin: 0, numMax: candidates.size())
608 .defaultValue<decltype(candidates)>(value: std::move(candidates))
609 .remaining()
610 .metaVariable(variable: metaVar)
611 .help(message: descr<DivisorMethod>());
612 applyingForwarder.registerHandler(handling: [](const action::SetChoice<DivisorMethod>& msg)
613 { setChoice<DivisorMethod>(msg.choice); });
614 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<DivisorMethod>& msg)
615 { runCandidates<DivisorMethod>(candidates: msg.candidates); });
616 versionLinks.emplace(args: VerLinkKey{name<ApplyNumeric>(), divisor::version()}, args: name<DivisorMethod>());
617 candidates = extractChoices<IntegralMethod>();
618 registry.emplace(args: name<IntegralMethod>(), args: Attr{.choices: candidates, .event: IntegralMethod{}});
619 subCLIAppNum
620 .addArgument(
621 fewArgs: shortPrefix + std::string{alias<IntegralMethod>()}, fewArgs: longPrefix + std::string{name<IntegralMethod>()})
622 .argsNum(numMin: 0, numMax: candidates.size())
623 .defaultValue<decltype(candidates)>(value: std::move(candidates))
624 .remaining()
625 .metaVariable(variable: metaVar)
626 .help(message: descr<IntegralMethod>());
627 applyingForwarder.registerHandler(handling: [](const action::SetChoice<IntegralMethod>& msg)
628 { setChoice<IntegralMethod>(msg.choice); });
629 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<IntegralMethod>& msg)
630 { runCandidates<IntegralMethod>(candidates: msg.candidates); });
631 versionLinks.emplace(args: VerLinkKey{name<ApplyNumeric>(), integral::version()}, args: name<IntegralMethod>());
632 candidates = extractChoices<PrimeMethod>();
633 registry.emplace(args: name<PrimeMethod>(), args: Attr{.choices: candidates, .event: PrimeMethod{}});
634 subCLIAppNum
635 .addArgument(fewArgs: shortPrefix + std::string{alias<PrimeMethod>()}, fewArgs: longPrefix + std::string{name<PrimeMethod>()})
636 .argsNum(numMin: 0, numMax: candidates.size())
637 .defaultValue<decltype(candidates)>(value: std::move(candidates))
638 .remaining()
639 .metaVariable(variable: metaVar)
640 .help(message: descr<PrimeMethod>());
641 applyingForwarder.registerHandler(handling: [](const action::SetChoice<PrimeMethod>& msg)
642 { setChoice<PrimeMethod>(msg.choice); });
643 applyingForwarder.registerHandler(handling: [](const action::RunCandidates<PrimeMethod>& msg)
644 { runCandidates<PrimeMethod>(candidates: msg.candidates); });
645 versionLinks.emplace(args: VerLinkKey{name<ApplyNumeric>(), prime::version()}, args: name<PrimeMethod>());
646
647 mainCLI.addSubParser(parser&: subCLIAppNum);
648}
649// NOLINTEND(google-build-using-namespace, readability-function-size)
650
651void Command::initializeNativeCLI()
652{
653 setupMainCLI();
654}
655
656void Command::initializeExtraCLI()
657{
658 setupSubCLI<reg_algo::ApplyAlgorithm>();
659 setupSubCLI<reg_dp::ApplyDesignPattern>();
660 setupSubCLI<reg_ds::ApplyDataStructure>();
661 setupSubCLI<reg_num::ApplyNumeric>();
662}
663
664void Command::frontEndHandler(const int argc, const char* const argv[])
665try
666{
667 std::unique_lock<std::mutex> parserLock(parserMtx);
668 mainCLI.clearUsed();
669 mainCLI.parseArgs(argc, argv);
670 precheck();
671
672 isParsed.store(i: true);
673 parserLock.unlock();
674 parserCond.notify_one();
675}
676catch (const std::exception& err)
677{
678 isParsed.store(i: true);
679 parserCond.notify_one();
680 isFaulty.store(i: true);
681 LOG_WRN << err.what();
682}
683
684void Command::backEndHandler()
685try
686{
687 if (std::unique_lock<std::mutex> parserLock(parserMtx); true)
688 {
689 parserCond.wait(lock&: parserLock, p: [this]() { return isParsed.load(); });
690 }
691
692 if (anySelected())
693 {
694 dispatchAll();
695 clearSelected();
696 }
697}
698catch (const std::exception& err)
699{
700 clearSelected();
701 isFaulty.store(i: true);
702 LOG_WRN << err.what();
703}
704
705void Command::precheck()
706{
707 for (auto& spec = taskDispatcher.nativeCategories;
708 const auto index : std::views::iota(0U, spec.size())
709 | std::views::filter([this](const auto i) { return mainCLI.isUsed(argName: toString(cat: static_cast<Category>(i))); }))
710 {
711 checkExcessArgs();
712 spec.set(position: index);
713 }
714
715 for (constexpr auto helpArgName = toString(cat: Category::help);
716 [[maybe_unused]] const auto& [subCLIName, categoryMap] : taskDispatcher.extraChoiceRegistry
717 | std::views::filter([this](const auto& subCLIPair)
718 { return mainCLI.isSubCommandUsed(subCLIPair.first) && (checkExcessArgs(), true); }))
719 {
720 const auto& subCLI = mainCLI.at<utility::argument::Argument>(name: subCLIName);
721 const bool notAssigned = !subCLI;
722 taskDispatcher.extraHelping = notAssigned || subCLI.isUsed(argName: helpArgName);
723 if (notAssigned)
724 {
725 return;
726 }
727
728 for ([[maybe_unused]] const auto& [categoryName, categoryAttr] : categoryMap
729 | std::views::filter([this, &subCLI](const auto& categoryPair)
730 { return subCLI.isUsed(argName: categoryPair.first) && (checkExcessArgs(), true); }))
731 {
732 for (const auto& choice : subCLI.get<std::vector<std::string>>(argName: categoryName))
733 {
734 std::visit(
735 visitor: utility::common::VisitorOverload{
736 [this, &choice](auto&& event)
737 { applyingForwarder.onMessage(action::SetChoice<std::decay_t<decltype(event)>>{choice}); }},
738 variants: categoryAttr.event);
739 }
740 }
741 }
742}
743
744bool Command::anySelected() const
745{
746 return !taskDispatcher.empty();
747}
748
749void Command::clearSelected()
750{
751 taskDispatcher.reset();
752}
753
754void Command::dispatchAll()
755{
756 if (!taskDispatcher.NativeManager::empty())
757 {
758 for (const auto& spec = taskDispatcher.nativeCategories;
759 const auto index :
760 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
761 {
762 builtInNotifier.notify(key: static_cast<Category>(index));
763 }
764 }
765
766 if (!taskDispatcher.ExtraManager::empty())
767 {
768 if (taskDispatcher.extraHelping)
769 {
770 if (auto whichUsed = std::views::keys(taskDispatcher.extraChoiceRegistry)
771 | std::views::filter([this](const auto& subCLIName)
772 { return mainCLI.isSubCommandUsed(subCLIName); });
773 std::ranges::distance(whichUsed) != 0)
774 {
775 std::cout << mainCLI.at<utility::argument::Argument>(name: *std::ranges::begin(whichUsed)).help().str()
776 << std::flush;
777 }
778 return;
779 }
780
781 for ([[maybe_unused]] const auto& [categoryName, categoryAttr] : taskDispatcher.extraChoiceRegistry
782 | std::views::filter([this](const auto& subCLIPair)
783 { return taskDispatcher.extraChecklist.at(subCLIPair.first).present(); })
784 | std::views::values | std::views::join)
785 {
786 std::visit(
787 visitor: utility::common::VisitorOverload{
788 [this, &candidates = categoryAttr.choices](auto&& event)
789 { applyingForwarder.onMessage(action::RunCandidates<std::decay_t<decltype(event)>>{candidates}); }},
790 variants: categoryAttr.event);
791 }
792 }
793}
794
795void Command::checkExcessArgs()
796{
797 if (anySelected())
798 {
799 clearSelected();
800 throw std::runtime_error{"Excessive arguments were found."};
801 }
802}
803
804template <typename Cat>
805std::vector<std::string> Command::extractChoices()
806{
807 constexpr auto refl = REFLECTION_STR("choice");
808 std::vector<std::string> choices{};
809 choices.reserve(n: utility::reflection::TypeInfo<Cat>::fields.size);
810 utility::reflection::TypeInfo<Cat>::fields.forEach(
811 [refl, &choices](const auto field)
812 {
813 static_assert(field.attrs.contains(refl) && (field.attrs.size == 1));
814 const auto attr = field.attrs.find(refl);
815 static_assert(attr.hasValue);
816 choices.emplace_back(attr.value);
817 });
818 return choices;
819}
820
821//! @brief Launch the TCP client for console mode.
822//! @param client - TCP client to be launched
823template <>
824void Command::launchClient<utility::socket::TCPSocket>(std::shared_ptr<utility::socket::TCPSocket>& client)
825{
826 client->subscribeRawMessage(
827 callback: [&client](char* const bytes, const std::size_t size)
828 {
829 try
830 {
831 MACRO_DEFER([]() { notifyClientOutputDone(); });
832 if (!client->stopRequested() && !onParsing4Client(bytes, size))
833 {
834 client->requestStop();
835 }
836 }
837 catch (const std::exception& err)
838 {
839 LOG_WRN << err.what();
840 }
841 });
842 client->toConnect(ip: view::info::currentTCPHost(), port: view::info::currentTCPPort());
843}
844
845//! @brief Launch the UDP client for console mode.
846//! @param client - UDP client to be launched
847template <>
848void Command::launchClient<utility::socket::UDPSocket>(std::shared_ptr<utility::socket::UDPSocket>& client)
849{
850 client->subscribeRawMessage(
851 callback: [&client](char* const bytes, const std::size_t size, const std::string& /*ip*/, const std::uint16_t /*port*/)
852 {
853 try
854 {
855 MACRO_DEFER([]() { notifyClientOutputDone(); });
856 if (!client->stopRequested() && !onParsing4Client(bytes, size))
857 {
858 client->requestStop();
859 }
860 }
861 catch (const std::exception& err)
862 {
863 LOG_WRN << err.what();
864 }
865 });
866 client->toReceive();
867 client->toConnect(ip: view::info::currentUDPHost(), port: view::info::currentUDPPort());
868}
869
870void Command::executeInConsole() const
871{
872 if (!configure::detail::activateHelper())
873 {
874 std::cout << "exit" << std::endl;
875 return;
876 }
877
878 const auto& pendingInputs = mainCLI.get<std::vector<std::string>>(argName: toString(cat: Category::console));
879 if (pendingInputs.empty())
880 {
881 return;
882 }
883
884 constexpr std::string_view greeting = "> ";
885 const auto session = std::make_unique<console::Console>(args: greeting);
886 auto udpClient = std::make_shared<utility::socket::UDPSocket>();
887 launchClient(client&: udpClient);
888 registerOnConsole(session&: *session, client&: udpClient);
889
890 for (const auto& input : pendingInputs)
891 {
892 try
893 {
894 using RetCode = console::Console::RetCode;
895 std::cout << greeting << input << std::endl;
896 if (session->optionExecutor(option: input) == RetCode::quit)
897 {
898 break;
899 }
900 }
901 catch (const std::exception& err)
902 {
903 LOG_WRN << err.what();
904 interactionLatency();
905 }
906 }
907 udpClient->toSend(message: buildDisconnectReq());
908 udpClient->toJoin();
909 interactionLatency();
910}
911
912void Command::showHelpMessage() const
913{
914 std::cout << mainCLI.help().str() << std::flush;
915}
916
917void Command::dumpConfiguration()
918{
919 std::cout << configure::dumpDefaultConfig() << std::endl;
920}
921
922void Command::displayVersionInfo() const
923{
924 validateDependencies();
925
926 const auto briefReview = std::format(
927 fmt: "\033[7m\033[49m{}"
928#ifndef NDEBUG
929 " DEBUG VERSION {} "
930#else
931 " RELEASE VERSION {} "
932#endif // NDEBUG
933 "\033[0m\nBuilt {} with {} for {} on {}.\n{}",
934 args: build::banner(),
935 args: mainCLI.version(),
936 args: build::revision(),
937 args: build::compiler(),
938 args: build::processor(),
939 args: build::date(),
940 args: build::copyright());
941 std::cout << briefReview << std::endl;
942}
943
944//! @brief Perform the specific operation for Category::console.
945template <>
946template <>
947void Command::LocalNotifier::Handler<Category::console>::execute() const
948{
949 inst.executeInConsole();
950}
951
952//! @brief Perform the specific operation for Category::dump.
953template <>
954template <>
955void Command::LocalNotifier::Handler<Category::dump>::execute() const
956{
957 inst.dumpConfiguration();
958}
959
960//! @brief Perform the specific operation for Category::help.
961template <>
962template <>
963void Command::LocalNotifier::Handler<Category::help>::execute() const
964{
965 inst.showHelpMessage();
966}
967
968//! @brief Perform the specific operation for Category::version.
969template <>
970template <>
971void Command::LocalNotifier::Handler<Category::version>::execute() const
972{
973 inst.displayVersionInfo();
974}
975
976void Command::validateDependencies() const
977{
978 const bool isNativeVerMatched = utility::common::areStringsEqual(
979 str1: mainCLI.version().data(),
980 str2: utility::argument::version(),
981 others: utility::benchmark::version(),
982 others: utility::common::version(),
983 others: utility::currying::version(),
984 others: utility::fsm::version(),
985 others: utility::io::version(),
986 others: utility::json::version(),
987 others: utility::macro::version(),
988 others: utility::memory::version(),
989 others: utility::reflection::version(),
990 others: utility::socket::version(),
991 others: utility::thread::version(),
992 others: utility::time::version());
993 if (!isNativeVerMatched)
994 {
995 throw std::runtime_error{std::format(
996 fmt: "Dependencies version number mismatch. Expected main version: {} ({}).",
997 args: mainCLI.title(),
998 args: mainCLI.version())};
999 }
1000
1001 const auto& choiceRegistry = taskDispatcher.extraChoiceRegistry;
1002 const bool isExtraVerMatched = (versionLinks.count(x: {subCLIAppAlgo.title(), subCLIAppAlgo.version()})
1003 == choiceRegistry.at(k: subCLIAppAlgo.title()).size())
1004 && (versionLinks.count(x: {subCLIAppDp.title(), subCLIAppDp.version()})
1005 == choiceRegistry.at(k: subCLIAppDp.title()).size())
1006 && (versionLinks.count(x: {subCLIAppDs.title(), subCLIAppDs.version()})
1007 == choiceRegistry.at(k: subCLIAppDs.title()).size())
1008 && (versionLinks.count(x: {subCLIAppNum.title(), subCLIAppNum.version()})
1009 == choiceRegistry.at(k: subCLIAppNum.title()).size());
1010 if (!isExtraVerMatched)
1011 {
1012 throw std::runtime_error{std::format(
1013 fmt: "Dependencies version number mismatch. Expected sub-version: {} ({}), {} ({}), {} ({}), {} ({}).",
1014 args: subCLIAppAlgo.title(),
1015 args: subCLIAppAlgo.version(),
1016 args: subCLIAppDp.title(),
1017 args: subCLIAppDp.version(),
1018 args: subCLIAppDs.title(),
1019 args: subCLIAppDs.version(),
1020 args: subCLIAppNum.title(),
1021 args: subCLIAppNum.version())};
1022 }
1023}
1024
1025void Command::enterConsoleMode()
1026try
1027{
1028 if (!configure::detail::activateHelper())
1029 {
1030 std::cout << "exit" << std::endl;
1031 return;
1032 }
1033 LOG_DBG << "Enter console mode.";
1034
1035 interactionLatency();
1036 const char* const userEnv = std::getenv(name: "USER"); // NOLINT(concurrency-mt-unsafe)
1037 const std::string userName = userEnv ? userEnv : "USER";
1038 std::array<char, HOST_NAME_MAX> hostName{};
1039 if (::gethostname(name: hostName.data(), len: hostName.size()) != 0)
1040 {
1041 std::strncpy(dest: hostName.data(), src: "HOSTNAME", n: hostName.size() - 1);
1042 hostName[HOST_NAME_MAX - 1] = '\0';
1043 }
1044 const auto greeting = userName + '@' + hostName.data() + " foo > ";
1045 const auto session = std::make_unique<console::Console>(args: greeting);
1046 auto tcpClient = std::make_shared<utility::socket::TCPSocket>();
1047 launchClient(client&: tcpClient);
1048 registerOnConsole(session&: *session, client&: tcpClient);
1049
1050 std::cout << build::banner() << std::endl;
1051 using RetCode = console::Console::RetCode;
1052 auto retCode = RetCode::success;
1053 do
1054 {
1055 try
1056 {
1057 retCode = session->readLine();
1058 }
1059 catch (const std::exception& err)
1060 {
1061 LOG_WRN << err.what();
1062 }
1063 session->setGreeting(greeting);
1064 interactionLatency();
1065 }
1066 while (retCode != RetCode::quit);
1067 tcpClient->toSend(message: buildDisconnectReq());
1068 tcpClient->toJoin();
1069 interactionLatency();
1070
1071 LOG_DBG << "Exit console mode.";
1072}
1073catch (const std::exception& err)
1074{
1075 LOG_ERR << err.what();
1076}
1077
1078auto Command::processConsoleInputs(const std::function<void()>& handling)
1079{
1080 using RetCode = console::Console::RetCode;
1081 auto retCode = RetCode::success;
1082 try
1083 {
1084 if (handling)
1085 {
1086 handling();
1087 }
1088 }
1089 catch (const std::exception& err)
1090 {
1091 retCode = RetCode::error;
1092 LOG_WRN << err.what();
1093 }
1094 interactionLatency();
1095 return retCode;
1096}
1097
1098template <typename Sock>
1099void Command::registerOnConsole(console::Console& session, std::shared_ptr<Sock>& client)
1100{
1101 static constexpr auto gracefulReset = []<help::ExtHelper Helper>() constexpr
1102 {
1103 using namespace help; // NOLINT(google-build-using-namespace)
1104 triggerEvent<Helper>(ExtEvent::reload);
1105 triggerEvent<Helper>(ExtEvent::startup);
1106 };
1107 const auto asyncReqSender = [&client](const auto& inputs)
1108 {
1109 return processConsoleInputs(
1110 handling: [&client, &inputs]()
1111 {
1112 auto reqBuffer = utility::common::base64Encode(data: std::accumulate(
1113 inputs.cbegin(),
1114 inputs.cend(),
1115 std::string{},
1116 [](const auto& acc, const auto& token) { return acc.empty() ? token : (acc + ' ' + token); }));
1117 client->toSend(std::move(reqBuffer));
1118 waitClientOutputDone();
1119 });
1120 };
1121
1122 session.registerOption(
1123 name: "refresh",
1124 description: "refresh the outputs",
1125 callback: [](const auto& /*inputs*/)
1126 {
1127 return processConsoleInputs(
1128 handling: []()
1129 {
1130 using log::Log;
1131 gracefulReset.template operator()<Log>();
1132 LOG_INF_F("Refreshed the {} outputs.", Log::name);
1133 });
1134 });
1135 session.registerOption(
1136 name: "reconnect",
1137 description: "reconnect to the servers",
1138 callback: [&client](const auto& /*inputs*/)
1139 {
1140 return processConsoleInputs(
1141 handling: [&client]()
1142 {
1143 client->toSend(buildDisconnectReq());
1144 client->toJoin();
1145 interactionLatency();
1146 client.reset();
1147
1148 using view::View;
1149 gracefulReset.template operator()<View>();
1150 client = std::make_shared<Sock>();
1151 launchClient(client);
1152 LOG_INF_F("Reconnected to the {} servers.", View::name);
1153 });
1154 });
1155
1156 auto supportedOptions = view::info::currentSupportedOptions();
1157 decltype(supportedOptions) validOptions{};
1158 for (auto iterator = supportedOptions.cbegin(); iterator != supportedOptions.cend();)
1159 {
1160 auto node = supportedOptions.extract(pos: iterator++);
1161 auto& key = node.key();
1162 key.erase(
1163 std::remove_if(key.begin(), key.end(), [l = std::locale{}](const auto c) { return std::isspace(c, l); }),
1164 key.cend());
1165 validOptions.insert(nh: std::move(node));
1166 }
1167 for (const auto& [name, description] : validOptions)
1168 {
1169 session.registerOption(name, description, callback: asyncReqSender);
1170 }
1171}
1172
1173bool Command::onParsing4Client(char* const bytes, const std::size_t size)
1174{
1175 return (size == 0) || view::View::Access().onParsing(bytes, size);
1176}
1177
1178void Command::waitClientOutputDone()
1179{
1180 view::View::Sync().waitTaskDone();
1181}
1182
1183void Command::notifyClientOutputDone()
1184{
1185 view::View::Sync().notifyTaskDone();
1186}
1187
1188std::string Command::buildDisconnectReq()
1189{
1190 return utility::common::base64Encode(data: view::exitSymbol);
1191}
1192
1193void Command::interactionLatency()
1194{
1195 constexpr auto latency = std::chrono::milliseconds{10};
1196 std::this_thread::sleep_for(rtime: latency);
1197}
1198
1199//! @brief Safely execute the command line interfaces using the given arguments.
1200//! @param argc - argument count
1201//! @param argv - argument vector
1202//! @return successful or failed to execute
1203bool executeCLI(const int argc, const char* const argv[])
1204try
1205{
1206 cliSem.acquire();
1207 const bool status = getInstance().execute(argc, argv);
1208 cliSem.release();
1209 return status;
1210}
1211catch (...)
1212{
1213 cliSem.release();
1214 throw;
1215}
1216} // namespace application::command
1217