1//! @file command.hpp
2//! @author ryftchen
3//! @brief The declarations (command) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#pragma once
8
9#include "action.hpp"
10#include "build.hpp"
11#include "console.hpp"
12
13#include "utility/include/argument.hpp"
14
15//! @brief The application module.
16namespace application // NOLINT(modernize-concat-nested-namespaces)
17{
18//! @brief Command-line-related functions in the application module.
19namespace command
20{
21//! @brief Instance title.
22inline constexpr std::string title = "commander";
23
24//! @brief Represent the maximum value of an enum.
25//! @tparam Enum - type of specific enum
26template <typename Enum>
27struct Bottom;
28
29//! @brief Enumerate specific native categories.
30enum class Category : std::uint8_t
31{
32 //! @brief Console.
33 console,
34 //! @brief Dump.
35 dump,
36 //! @brief Help.
37 help,
38 //! @brief Version.
39 version
40};
41//! @brief Store the maximum value of the Category enum.
42template <>
43struct Bottom<Category>
44{
45 //! @brief Maximum value of the Category enum.
46 static constexpr std::uint8_t value{4};
47};
48
49//! @brief Gather and notify handlers.
50//! @tparam Key - type of key
51//! @tparam Inst - type of instance
52template <typename Key, typename Inst>
53class Notifier
54{
55public:
56 //! @brief The base procedure when notified.
57 class Operation
58 {
59 public:
60 //! @brief Destroy the Operation object.
61 virtual ~Operation() = default;
62
63 //! @brief Perform the specific operation.
64 virtual void execute() const = 0;
65 };
66 //! @brief The procedure when notified.
67 //! @tparam CRTP - type of derived class
68 template <typename CRTP>
69 class OperationImpl : public Operation
70 {
71 public:
72 //! @brief Perform the specific operation.
73 void execute() const override { static_cast<const CRTP&>(*this).execute(); }
74 };
75 //! @brief The handler used to trigger a procedure when notified.
76 //! @tparam key - specific key
77 template <Key key>
78 class Handler : public OperationImpl<Handler<key>>
79 {
80 public:
81 //! @brief Construct a new Handler object.
82 //! @param inst - involved instance
83 explicit Handler(const Inst& inst) : inst{inst} {}
84
85 //! @brief Perform the specific operation.
86 void execute() const override;
87
88 private:
89 //! @brief The involved instance.
90 const Inst& inst{};
91 };
92
93 //! @brief Attach a handler with a specific key to the notifier.
94 //! @param key - specific key
95 //! @param handler - handler to be attached
96 void attach(const Key key, std::shared_ptr<Operation> handler);
97 //! @brief Notify the handler associated with the given key.
98 //! @param key - specific key
99 void notify(const Key key) const;
100
101private:
102 //! @brief Map of handlers identified by a key.
103 std::map<Key, std::shared_ptr<Operation>> handlers{};
104};
105
106//! @brief Execute the command line.
107class Command final
108{
109public:
110 //! @brief Destroy the Command object.
111 ~Command();
112 //! @brief Construct a new Command object.
113 Command(const Command&) = delete;
114 //! @brief Construct a new Command object.
115 Command(Command&&) = delete;
116 //! @brief The operator (=) overloading of Command class.
117 //! @return reference of the Command object
118 Command& operator=(const Command&) = delete;
119 //! @brief The operator (=) overloading of Command class.
120 //! @return reference of the Command object
121 Command& operator=(Command&&) = delete;
122
123 friend Command& getInstance();
124
125private:
126 //! @brief Construct a new Command object.
127 Command();
128
129 //! @brief Mutex for controlling parser.
130 mutable std::mutex parserMtx;
131 //! @brief The synchronization condition for foreground and background. Use with parserMtx.
132 std::condition_variable parserCond;
133 //! @brief Flag to indicate whether parsing of arguments is completed.
134 std::atomic_bool isParsed{false};
135 //! @brief Parse argument helper for commander.
136 utility::argument::Argument mainCLI{"foo", build::version()};
137 //! @brief Parse argument helper to apply algorithm.
138 utility::argument::Argument subCLIAppAlgo{action::name<reg_algo::ApplyAlgorithm>(), reg_algo::version()};
139 //! @brief Parse argument helper to apply design pattern.
140 utility::argument::Argument subCLIAppDp{action::name<reg_dp::ApplyDesignPattern>(), reg_dp::version()};
141 //! @brief Parse argument helper to apply data structure.
142 utility::argument::Argument subCLIAppDs{action::name<reg_ds::ApplyDataStructure>(), reg_ds::version()};
143 //! @brief Parse argument helper to apply numeric.
144 utility::argument::Argument subCLIAppNum{action::name<reg_num::ApplyNumeric>(), reg_num::version()};
145 //! @brief The short prefix for the option.
146 const std::string shortPrefix{"-"};
147 //! @brief The Long prefix for the option.
148 const std::string longPrefix{"--"};
149 //! @brief The meta variable for the option.
150 static constexpr std::string_view metaVar{"OPT"};
151 //! @brief Flag to indicate whether the command is faulty.
152 std::atomic_bool isFaulty{false};
153
154 friend bool executeCLI(const int argc, const char* const argv[]);
155 //! @brief Interface used to execute.
156 //! @param argc - argument count
157 //! @param argv - argument vector
158 //! @return successful or failed to execute
159 bool execute(const int argc, const char* const argv[]);
160 //! @brief Initialize the parse argument helpers for native.
161 void initializeNativeCLI();
162 //! @brief Initialize the parse argument helpers for extra.
163 void initializeExtraCLI();
164 //! @brief Setup the main command line interface.
165 void setupMainCLI();
166 //! @brief Setup the sub-command line interface.
167 //! @tparam SubCLI - type of type of sub-cli
168 template <typename SubCLI>
169 void setupSubCLI();
170 //! @brief Front-end handler for parsing command line arguments.
171 //! @param argc - argument count
172 //! @param argv - argument vector
173 void frontEndHandler(const int argc, const char* const argv[]);
174 //! @brief Back-end handler for performing the specific tasks.
175 void backEndHandler();
176 //! @brief Precheck the native type or extra type task.
177 void precheck();
178 //! @brief Check whether any type tasks exist.
179 //! @return any type tasks exist or do not exist
180 bool anySelected() const;
181 //! @brief Clear all type tasks.
182 void clearSelected();
183 //! @brief Dispatch all specific tasks.
184 void dispatchAll();
185 //! @brief Check for excessive arguments.
186 void checkExcessArgs();
187 //! @brief Execute the command line of console mode.
188 void executeInConsole() const;
189 //! @brief Show help message.
190 void showHelpMessage() const;
191 //! @brief Dump configuration.
192 static void dumpConfiguration();
193 //! @brief Display version information.
194 void displayVersionInfo() const;
195 //! @brief Validate the version of all dependencies.
196 void validateDependencies() const;
197
198 //! @brief Map the alias name.
199 //! @param cat - native category
200 //! @return alias name
201 static consteval std::string_view mappedAlias(const Category cat);
202 //! @brief Map the description.
203 //! @param cat - native category
204 //! @return description
205 static consteval std::string_view mappedDescr(const Category cat);
206 //! @brief Extract all choices in the sub-cli's category.
207 //! @tparam Cat - type of sub-cli's category
208 //! @return all choices
209 template <typename Cat>
210 static std::vector<std::string> extractChoices();
211
212 //! @brief Manage tasks.
213 class TaskManager
214 {
215 public:
216 //! @brief Destroy the TaskManager object.
217 virtual ~TaskManager() = default;
218
219 //! @brief Check whether any tasks do not exist.
220 //! @return any tasks do not exist or exist
221 [[nodiscard]] virtual bool empty() const = 0;
222 //! @brief Reset bit flags that manage all tasks.
223 virtual void reset() = 0;
224 };
225 //! @brief Manage native categories.
226 class NativeManager : virtual public TaskManager
227 {
228 public:
229 //! @brief Bit flags for managing native categories.
230 std::bitset<Bottom<Category>::value> nativeCategories;
231
232 //! @brief Check whether any native categories do not exist.
233 //! @return any native categories do not exist or exist
234 [[nodiscard]] bool empty() const override { return nativeCategories.none(); }
235 //! @brief Reset bit flags that manage native categories.
236 void reset() override { nativeCategories.reset(); }
237 };
238 //! @brief Manage extra choices of sub-cli.
239 class ExtraManager : virtual public TaskManager
240 {
241 public:
242 //! @brief Alias for the attribute of the registered sub-cli's category.
243 struct Attr
244 {
245 //! @brief The candidates for the choice.
246 const std::vector<std::string> choices;
247 //! @brief The internal event for applying.
248 const action::EventType event;
249 };
250 //! @brief Alias for the map of sub-cli's category name and Attr.
251 using CategoryMap = std::map<std::string, Attr>;
252 //! @brief Mapping table of all extra choices. Fill as needed.
253 std::map<std::string, CategoryMap> extraChoiceRegistry;
254
255 //! @brief Wrap interfaces to check for existing and reset extra choices.
256 struct Intf
257 {
258 Intf(std::function<bool()> presentCb, std::function<void()> clearCb) :
259 present{std::move(presentCb)}, clear{std::move(clearCb)}
260 {
261 if (!present || !clear)
262 {
263 throw std::runtime_error{"Invalid sub-command interfaces are being used."};
264 }
265 }
266
267 //! @brief Check the existence status of the extra choice.
268 const std::function<bool()> present;
269 //! @brief Reset control of the extra choice.
270 const std::function<void()> clear;
271 };
272 //! @brief Existence status and reset control of the sub-cli to which the extra choices belong.
273 std::map<std::string, Intf> extraChecklist;
274 //! @brief Flag for help only.
275 bool extraHelping{false};
276
277 //! @brief Check whether any extra choices do not exist.
278 //! @return any extra choices do not exist or exist
279 [[nodiscard]] bool empty() const override
280 {
281 return !extraHelping
282 && std::ranges::none_of(extraChecklist, [](const auto& pair) { return pair.second.present(); });
283 }
284 //! @brief Reset bit flags that manage extra choices.
285 void reset() override
286 {
287 extraHelping = false;
288 std::ranges::for_each(extraChecklist, [](const auto& pair) { pair.second.clear(); });
289 }
290 };
291 //! @brief Schedule all managed tasks.
292 class TaskDispatcher : public NativeManager, public ExtraManager
293 {
294 public:
295 //! @brief Check whether any tasks do not exist.
296 //! @return any tasks do not exist or exist
297 [[nodiscard]] bool empty() const final { return NativeManager::empty() && ExtraManager::empty(); }
298 //! @brief Reset bit flags that manage all tasks.
299 void reset() final
300 {
301 NativeManager::reset();
302 ExtraManager::reset();
303 }
304 } /** @brief Dispatch all types of tasks. */ taskDispatcher{};
305
306 friend class Notifier<Category, Command>;
307 //! @brief Alias for the local notifier.
308 using LocalNotifier = Notifier<Category, Command>;
309 //! @brief Local notification for native type tasks.
310 LocalNotifier builtInNotifier{};
311 //! @brief Forward messages for extra type tasks.
312 action::MessageForwarder applyingForwarder{};
313 //! @brief Alias for the pair of the sub-cli name and the sub-cli version.
314 using VerLinkKey = std::pair<std::string, std::string>;
315 //! @brief Mapping hash value for the related versions.
316 struct VerLinkHash
317 {
318 //! @brief The operator (()) overloading of VerLinkHash struct.
319 //! @param key - pair of the sub-cli name and the sub-cli version
320 //! @return hash value
321 std::size_t operator()(const VerLinkKey& key) const
322 {
323 constexpr std::size_t magicNumber = 0x9E3779B9;
324 constexpr std::size_t leftShift = 6;
325 constexpr std::size_t rightShift = 2;
326 const std::size_t hash1 = std::hash<std::string>()(key.first);
327 const std::size_t hash2 = std::hash<std::string>()(key.second);
328 std::size_t seed = 0;
329 seed ^= hash1 + magicNumber + (seed << leftShift) + (seed >> rightShift);
330 seed ^= hash2 + magicNumber + (seed << leftShift) + (seed >> rightShift);
331 return seed;
332 }
333 };
334 //! @brief Mapping table of related versions. Fill as needed.
335 std::unordered_multimap<VerLinkKey, std::string, VerLinkHash> versionLinks;
336
337 //! @brief Go to console mode for troubleshooting.
338 static void enterConsoleMode();
339 //! @brief Register the command line to console mode.
340 //! @tparam Sock - type of client
341 //! @param session - console to be registered
342 //! @param client - client used to send
343 template <typename Sock>
344 static void registerOnConsole(console::Console& session, std::shared_ptr<Sock>& client);
345 //! @brief Launch the client for console mode.
346 //! @tparam Sock - type of client
347 //! @param client - client to be launched
348 template <typename Sock>
349 static void launchClient(std::shared_ptr<Sock>& client);
350 //! @brief Process the inputs in console mode.
351 //! @param handling - handling for inputs
352 //! @return console return code
353 static auto processConsoleInputs(const std::function<void()>& handling);
354 //! @brief Parse the message inside the client in console mode.
355 //! @param bytes - message buffer
356 //! @param size - message length
357 //! @return need to continue parsing or not
358 static bool onParsing4Client(char* const bytes, const std::size_t size);
359 //! @brief Await outside the client in console mode.
360 static void waitClientOutputDone();
361 //! @brief Awaken inside the client in console mode.
362 static void notifyClientOutputDone();
363 //! @brief Build the exit request message in console mode.
364 //! @return exit request message
365 static std::string buildDisconnectRequest();
366 //! @brief Console latency in the millisecond range.
367 static void interactionLatency();
368};
369
370extern bool executeCLI(const int argc, const char* const argv[]);
371} // namespace command
372} // namespace application
373