ChainCLI
A modern C++20 command-line interface library
Loading...
Searching...
No Matches
command.h
1/*
2 * Copyright 2025 Dominik Czekai
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18#include "argument_group.h"
19#include "cli_context.h"
20#include "flag_argument.h"
21#include "option_argument.h"
22#include "positional_argument.h"
23#include <functional>
24#include <map>
25#include <memory>
26#include <ostream>
27#include <string_view>
28
29namespace cli::commands
30{
31
36{
37 friend std::ostream &operator<<(std::ostream &out, const Command &cmd);
38
39public:
41
47 Command(std::string_view id, std::string_view short_desc, std::string_view long_desc,
48 std::unique_ptr<std::function<void(const CliContext &)>> actionPtr)
49 : identifier(id), shortDescription(short_desc), longDescription(long_desc),
50 executePtr(std::move(actionPtr))
51 {
52 }
53
59 Command(std::string_view id, std::string_view short_desc, std::string_view long_desc,
60 std::function<void(const CliContext &)> action)
61 : identifier(id), shortDescription(short_desc), longDescription(long_desc),
62 executePtr(std::make_unique<std::function<void(const CliContext &)>>(std::move(action)))
63 {
64 }
65
68 explicit Command(std::string_view id)
69 : identifier(id), shortDescription(""), longDescription(""), executePtr(nullptr)
70 {
71 }
72
73 // Movable
74 Command(Command &&) noexcept = default;
75 Command &operator=(Command &&) noexcept = default;
76
77 // Non-copyable
78 Command(const Command &) = delete;
79 Command &operator=(const Command &) = delete;
80
81 virtual ~Command() = default;
82
83#pragma region Accessors
84
87 [[nodiscard]] constexpr std::string_view getIdentifier() const noexcept { return identifier; }
88
91 [[nodiscard]] constexpr std::string_view getShortDescription() const noexcept
92 {
93 return shortDescription;
94 }
95
98 [[nodiscard]] constexpr std::string_view getLongDescription() const noexcept
99 {
100 return longDescription;
101 }
102
105 [[nodiscard]] bool hasExecutionFunction() const noexcept { return executePtr.get(); }
106
109 [[nodiscard]] const std::vector<std::shared_ptr<PositionalArgumentBase>> &
110 getPositionalArguments() const noexcept
111 {
112 return positionalArguments;
113 }
114
119 [[nodiscard]] const std::vector<std::shared_ptr<OptionArgumentBase>> &getOptionArguments()
120 const noexcept
121 {
122 return optionArguments;
123 }
124
129 [[nodiscard]] const std::vector<std::shared_ptr<FlagArgument>> &getFlagArguments()
130 const noexcept
131 {
132 return flagArguments;
133 }
134
138 [[nodiscard]] const std::vector<std::unique_ptr<ArgumentGroup>> &getArgumentGroups()
139 const noexcept
140 {
141 return argumentGroups;
142 }
143
149 [[nodiscard]] std::string_view getDocStringShort() const;
150
156 [[nodiscard]] std::string_view getDocStringLong() const;
157
161 [[nodiscard]] Command *getSubCommand(std::string_view id);
162
166 [[nodiscard]] const Command *getSubCommand(std::string_view id) const;
167
170 [[nodiscard]] auto &getSubCommands() { return subCommands; }
171
174 [[nodiscard]] auto const &getSubCommands() const { return subCommands; }
175
176#pragma endregion Accessor
177
180 void execute(const CliContext &context) const;
181
182#pragma region ChainingMethods
186 Command &withShortDescription(std::string_view desc);
187
191 Command &withLongDescription(std::string_view desc);
192
199 template <typename T>
201 {
202 safeAddToArgGroup(arg);
203 positionalArguments.push_back(arg);
204 return *this;
205 }
206
214 {
215 return withPositionalArgument(std::make_shared<PositionalArgument<T>>(std::move(arg)));
216 }
217
225 {
226 return withPositionalArgument(std::make_shared<PositionalArgument<T>>(arg));
227 }
228
234 template <typename T> Command &withOptionArgument(std::shared_ptr<OptionArgument<T>> arg)
235 {
236 safeAddToArgGroup(arg);
237 optionArguments.push_back(arg);
238 return *this;
239 }
240
247 template <typename T> Command &withOptionArgument(OptionArgument<T> &&arg)
248 {
249 return withOptionArgument(std::make_shared<OptionArgument<T>>(std::move(arg)));
250 }
251
258 template <typename T> Command &withOptionArgument(OptionArgument<T> &arg)
259 {
260 return withOptionArgument(std::make_shared<OptionArgument<T>>(arg));
261 }
262
268 Command &withFlagArgument(std::shared_ptr<FlagArgument> arg);
269
276
283
287 Command &withExecutionFunc(std::unique_ptr<std::function<void(const CliContext &)>> actionPtr);
288
292 Command &withExecutionFunc(std::function<void(const CliContext &)> &&action);
293
297 Command &withSubCommand(std::unique_ptr<Command> subCommandPtr);
298
299 Command &withSubCommand(Command &&subCommand);
300
305 template <typename... Args>
306 requires((sizeof...(Args) > 0) &&
307 (std::derived_from<std::remove_cvref_t<Args>, ArgumentBase> && ...))
308 Command &withExclusiveGroup(Args &&...args);
309
314 template <typename... Args>
315 requires((sizeof...(Args) > 0) &&
316 (std::derived_from<std::remove_cvref_t<Args>, ArgumentBase> && ...))
317 Command &withInclusiveGroup(Args &&...args);
318#pragma endregion ChainingMethods
319
320private:
321 size_t indexForNewArgGroup{0};
322 void safeAddToArgGroup(const std::shared_ptr<ArgumentBase> &arg);
323 void addArgGroup(const ArgumentGroup &argGroup);
324
325 std::string identifier;
326 std::string shortDescription;
327 std::string longDescription;
328 std::unique_ptr<std::function<void(const CliContext &)>> executePtr;
329
330 // arguments
331 std::vector<std::shared_ptr<PositionalArgumentBase>> positionalArguments;
332 std::vector<std::shared_ptr<OptionArgumentBase>> optionArguments;
333 std::vector<std::shared_ptr<FlagArgument>> flagArguments;
334 std::vector<std::unique_ptr<ArgumentGroup>> argumentGroups;
335
336 std::string docStringShort; // cached short doc string
337 std::string docStringLong; // cached long doc string
338
339 std::map<std::string, std::unique_ptr<Command>, std::less<>> subCommands;
340};
341
345class MalformedCommandException : public std::runtime_error
346{
347public:
348 explicit MalformedCommandException(const Command &cmd, const std::string &msg = "")
349 : std::runtime_error(buildMessage(cmd, msg)), malformedCmd(&cmd)
350 {
351 }
352
353 const Command &command() const noexcept { return *malformedCmd; }
354
355private:
356 const Command *malformedCmd;
357
358 static std::string buildMessage(const Command &cmd, const std::string &msg);
359};
360
361template <typename... Args>
362 requires((sizeof...(Args) > 0) &&
363 (std::derived_from<std::remove_cvref_t<Args>, ArgumentBase> && ...))
365{
366 argumentGroups.emplace_back(std::make_unique<ExclusiveGroup>(std::forward<Args>(args)...));
367 addArgGroup(*argumentGroups.back());
368 indexForNewArgGroup++;
369 return *this;
370}
371
372template <typename... Args>
373 requires((sizeof...(Args) > 0) &&
374 (std::derived_from<std::remove_cvref_t<Args>, ArgumentBase> && ...))
376{
377 argumentGroups.emplace_back(std::make_unique<InclusiveGroup>(std::forward<Args>(args)...));
378 addArgGroup(*argumentGroups.back());
379 indexForNewArgGroup++;
380 return *this;
381}
382
383} // namespace cli::commands
Represents the context of a command-line interface (CLI) invocation and as such contains the parsed v...
Definition cli_context.h:39
Base class for command-line arguments.
Definition argument.h:41
Represents a command in the CLI application.
Definition command.h:36
Command & withSubCommand(std::unique_ptr< Command > subCommandPtr)
Add a sub-command to this command.
Definition command.cpp:104
const std::vector< std::shared_ptr< FlagArgument > > & getFlagArguments() const noexcept
Get the flag arguments for the command.
Definition command.h:129
std::string_view getDocStringLong() const
Get the long documentation string for the command.
Definition command.cpp:39
Command(std::string_view id, std::string_view short_desc, std::string_view long_desc, std::unique_ptr< std::function< void(const CliContext &)> > actionPtr)
Construct a new Command object.
Definition command.h:47
Command & withPositionalArgument(PositionalArgument< T > &&arg)
Add a positional argument to the command.
Definition command.h:213
Command & withOptionArgument(std::shared_ptr< OptionArgument< T > > arg)
Add an option argument to the command.
Definition command.h:234
bool hasExecutionFunction() const noexcept
Check if the command has an execution function.
Definition command.h:105
Command(std::string_view id, std::string_view short_desc, std::string_view long_desc, std::function< void(const CliContext &)> action)
Construct a new Command object.
Definition command.h:59
const std::vector< std::shared_ptr< PositionalArgumentBase > > & getPositionalArguments() const noexcept
Get the positional arguments for the command.
Definition command.h:110
Command & withShortDescription(std::string_view desc)
Set the short description for the command.
Definition command.cpp:62
Command & withOptionArgument(OptionArgument< T > &&arg)
Add an option argument to the command.
Definition command.h:247
constexpr std::string_view getIdentifier() const noexcept
Get the unique identifier for the command.
Definition command.h:87
Command(std::string_view id)
Construct a new Command object.
Definition command.h:68
Command & withExclusiveGroup(Args &&...args)
Add a sub-command to this command.
Definition command.h:364
Command & withOptionArgument(OptionArgument< T > &arg)
Add an option argument to the command.
Definition command.h:258
constexpr std::string_view getShortDescription() const noexcept
Get the short description of the command.
Definition command.h:91
Command & withExecutionFunc(std::unique_ptr< std::function< void(const CliContext &)> > actionPtr)
Set the execution function for the command.
Definition command.cpp:91
Command & withPositionalArgument(std::shared_ptr< PositionalArgument< T > > arg)
Add a positional argument to the command.
Definition command.h:200
constexpr std::string_view getLongDescription() const noexcept
Get the long description of the command.
Definition command.h:98
Command & withLongDescription(std::string_view desc)
Set the long description for the command.
Definition command.cpp:68
auto & getSubCommands()
Get all sub-commands of the command.
Definition command.h:170
const std::vector< std::shared_ptr< OptionArgumentBase > > & getOptionArguments() const noexcept
Get the option arguments for the command.
Definition command.h:119
Command & withInclusiveGroup(Args &&...args)
Add an inclusive argument group to this command.
Definition command.h:375
Command * getSubCommand(std::string_view id)
Get a sub-command by its identifier.
Definition command.cpp:145
Command & withPositionalArgument(PositionalArgument< T > &arg)
Add a positional argument to the command.
Definition command.h:224
Command & withFlagArgument(std::shared_ptr< FlagArgument > arg)
Add a flag argument to the command.
Definition command.cpp:74
std::string_view getDocStringShort() const
Get the short documentation string for the command.
Definition command.cpp:29
const std::vector< std::unique_ptr< ArgumentGroup > > & getArgumentGroups() const noexcept
Get the argument groups for the command.
Definition command.h:138
void execute(const CliContext &context) const
Execute the command.
Definition command.cpp:49
auto const & getSubCommands() const
Get all sub-commands of the command.
Definition command.h:174
Represents a flag argument in the CLI.
Definition flag_argument.h:31
Exception thrown when a command is malformed.
Definition command.h:346
Represents option arguments in the CLI.
Definition option_argument.h:64
Represents positional arguments in the CLI.
Definition positional_argument.h:57
Documentation writer for CLI commands. Consists of formatters for commands and arguments.
Definition docwriting.h:34