ChainCLI
A modern C++20 command-line interface library
Loading...
Searching...
No Matches
cli_context.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 <any>
19#include <functional>
20#include <map>
21#include <memory>
22#include <stdexcept>
23#include <string>
24#include <unordered_set>
25
26#ifdef CHAIN_CLI_VERBOSE
27#include <iostream>
28#endif
29
30#include "logging/logger.h"
31#include "context_exception.h"
32
33namespace cli
34{
35
39{
40public:
46 explicit CliContext(std::unique_ptr<std::unordered_map<std::string, std::any>> posArgs,
47 std::unique_ptr<std::unordered_map<std::string, std::any>> optArgs,
48 std::unique_ptr<std::unordered_set<std::string>> flagArgs,
50 : logger(logger), positionalArgs(std::move(posArgs)), optionArgs(std::move(optArgs)),
51 flagArgs(std::move(flagArgs))
52 {
53 }
54
55 // Non-copyable
56 CliContext(const CliContext &) = delete;
57 CliContext &operator=(const CliContext &) = delete;
58
62 bool isArgPresent(const std::string &argName) const;
63
67 bool isOptionArgPresent(const std::string &argName) const;
68
72 bool isPositionalArgPresent(const std::string &argName) const;
73
77 bool isFlagPresent(const std::string &argName) const;
78
84 template <typename T> T getPositionalArg(const std::string &argName) const
85 {
86#ifdef CHAIN_CLI_VERBOSE
87 std::cout << "Getting positional argument '" << argName << "' as type " << typeid(T).name() << "\n";
88#endif
89 return getAnyCast<T>(argName, *positionalArgs);
90 }
91
97 template <typename T> void getPositionalArg(const std::string &argName, T &out) const
98 {
99 out = getAnyCast<T>(argName, *positionalArgs);
100 }
101
107 template <typename T> T getOptionArg(const std::string &argName) const
108 {
109#ifdef CHAIN_CLI_VERBOSE
110 std::cout << "Getting option argument '" << argName << "' as type " << typeid(T).name() << "\n";
111#endif
112 return getAnyCast<T>(argName, *optionArgs);
113 }
114
120 template <typename T> void getOptionArg(const std::string &argName, T &out) const
121 {
122 out = getAnyCast<T>(argName, *optionArgs);
123 }
124
130 template <typename T> std::vector<T> getRepeatableOptionArg(const std::string &argName) const
131 {
132#ifdef CHAIN_CLI_VERBOSE
133 std::cout << "Getting repeatable option argument '" << argName << "' as vector of type " << typeid(T).name() << "\n";
134#endif
135 auto it = optionArgs->find(argName);
136 if (it == optionArgs->end())
137 throw MissingArgumentException(argName, *optionArgs);
138
139 try
140 {
141 auto anyVec = getAnyCast<std::vector<std::any>>(argName, *optionArgs);
142 std::vector<T> result;
143 result.reserve(anyVec.size());
144 for (const auto &elem : anyVec)
145 {
146 result.push_back(std::any_cast<T>(elem));
147 }
148
149 return result;
150 }
151 catch (const std::bad_any_cast &)
152 {
153 throw InvalidArgumentTypeException(argName, typeid(std::vector<T>), it->second.type());
154 }
155 }
156
163 template <typename T>
164 std::vector<T> getRepeatablePositionalArg(const std::string &argName) const
165 {
166#ifdef CHAIN_CLI_VERBOSE
167 std::cout << "Getting repeatable positional argument '" << argName << "' as vector of type " << typeid(T).name() << "\n";
168#endif
169 auto it = positionalArgs->find(argName);
170 if (it == positionalArgs->end())
171 throw MissingArgumentException(argName, *positionalArgs);
172
173 try
174 {
175 auto anyVec = getAnyCast<std::vector<std::any>>(argName, *positionalArgs);
176 std::vector<T> result;
177 result.reserve(anyVec.size());
178 for (const auto &elem : anyVec)
179 {
180 result.push_back(std::any_cast<T>(elem));
181 }
182
183 return result;
184 }
185 catch (const std::bad_any_cast &)
186 {
187 throw InvalidArgumentTypeException(argName, typeid(std::vector<T>), it->second.type());
188 }
189 }
190
196 template <typename T> T getArg(const std::string &argName) const
197 {
198#ifdef CHAIN_CLI_VERBOSE
199 std::cout << "Getting any argument '" << argName << "' as type " << typeid(T).name() << "\n";
200#endif
201 if (isPositionalArgPresent(argName))
202 {
203 return getAnyCast<T>(argName, *positionalArgs);
204 }
205 else if (isOptionArgPresent(argName))
206 {
207 return getAnyCast<T>(argName, *optionArgs);
208 }
209 else
210 {
211 throw MissingArgumentException(argName, *positionalArgs);
212 }
213 }
214
220 template <typename T> auto getRepeatableArg(const std::string &argName) const
221 {
222#ifdef CHAIN_CLI_VERBOSE
223 std::cout << "Getting repeatable argument '" << argName << "' as vector of type " << typeid(T).name() << "\n";
224#endif
225 if (isPositionalArgPresent(argName))
226 {
227 return getRepeatablePositionalArg<T>(argName);
228 }
229 else if (isOptionArgPresent(argName))
230 {
231 return getRepeatableOptionArg<T>(argName);
232 }
233 else
234 {
235 throw MissingArgumentException(argName, *positionalArgs);
236 }
237 }
238
239 logging::AbstractLogger &Logger() const
240 {
241 return logger;
242 }
243
244private:
246 std::unique_ptr<std::unordered_map<std::string, std::any>> positionalArgs;
247 std::unique_ptr<std::unordered_map<std::string, std::any>> optionArgs;
248 std::unique_ptr<std::unordered_set<std::string>> flagArgs;
249
250 template <typename T>
251 static T getAnyCast(const std::string &name, std::unordered_map<std::string, std::any> &dict)
252 {
253 try
254 {
255 auto it = dict.find(name);
256 if (it == dict.end())
257 {
258 throw MissingArgumentException(name, dict);
259 }
260 return std::any_cast<T>(it->second);
261 }
262 catch (const std::bad_any_cast &)
263 {
264 throw InvalidArgumentTypeException(name, typeid(T), dict.at(name).type());
265 }
266 }
267};
268
269} // namespace cli
Represents the context of a command-line interface (CLI) invocation and as such contains the parsed v...
Definition cli_context.h:39
CliContext(std::unique_ptr< std::unordered_map< std::string, std::any > > posArgs, std::unique_ptr< std::unordered_map< std::string, std::any > > optArgs, std::unique_ptr< std::unordered_set< std::string > > flagArgs, cli::logging::AbstractLogger &logger)
Constructs a new CliContext object from the passed argument maps.
Definition cli_context.h:46
T getArg(const std::string &argName) const
Gets the value of an argument.
Definition cli_context.h:196
bool isFlagPresent(const std::string &argName) const
Checks if a flag with the given name is present in the context.
Definition cli_context.cpp:36
void getOptionArg(const std::string &argName, T &out) const
Gets the value of an optional argument and stores it in the provided output variable.
Definition cli_context.h:120
std::vector< T > getRepeatableOptionArg(const std::string &argName) const
Gets all values of a repeatable option argument.
Definition cli_context.h:130
std::vector< T > getRepeatablePositionalArg(const std::string &argName) const
Gets all values of a repeatable positional argument.
Definition cli_context.h:164
T getPositionalArg(const std::string &argName) const
Gets the value of a positional argument.
Definition cli_context.h:84
bool isArgPresent(const std::string &argName) const
Checks if an argument with the given name is present in the context.
Definition cli_context.cpp:41
void getPositionalArg(const std::string &argName, T &out) const
Gets the value of a positional argument and stores it in the provided output variable.
Definition cli_context.h:97
bool isOptionArgPresent(const std::string &argName) const
Checks if an optional argument with the given name is present in the context.
Definition cli_context.cpp:26
T getOptionArg(const std::string &argName) const
Gets the value of an optional argument.
Definition cli_context.h:107
auto getRepeatableArg(const std::string &argName) const
Gets all values of a repeatable argument.
Definition cli_context.h:220
bool isPositionalArgPresent(const std::string &argName) const
Checks if a positional argument with the given name is present in the context.
Definition cli_context.cpp:31
Thrown when an argument type that was requested is not the one that was parsed.
Definition context_exception.h:42
Thrown when an argument that was requested is missing in the context.
Definition context_exception.h:27
Abstract base class for logger implementations.
Definition logger.h:31