CLI11 2.7.2
C++11 Command Line Interface Parser
Loading...
Searching...
No Matches
Validators_inl.hpp
1// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9// IWYU pragma: private, include "CLI/CLI.hpp"
10#include "../Validators.hpp"
11
12#include "../Encoding.hpp"
13#include "../Macros.hpp"
14#include "../StringTools.hpp"
15#include "../TypeTools.hpp"
16
17// [CLI11:public_includes:set]
18#include <functional>
19#include <stdexcept>
20#include <string>
21#include <system_error>
22#include <utility>
23// [CLI11:public_includes:end]
24
25namespace CLI {
26// [CLI11:validators_inl_hpp:verbatim]
27
28CLI11_INLINE Validator::Validator(std::string validator_desc, std::function<std::string(std::string &)> func)
29 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {}
30
31CLI11_INLINE Validator::Validator(std::function<std::string(std::string &)> op,
32 std::string validator_desc,
33 std::string validator_name)
34 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
35 name_(std::move(validator_name)) {}
36
37CLI11_INLINE std::string Validator::operator()(std::string &str) const {
38 std::string retstring;
39 if(active_) {
40 if(non_modifying_) {
41 std::string value = str;
42 retstring = func_(value);
43 } else {
44 retstring = func_(str);
45 }
46 }
47 return retstring;
48}
49
50CLI11_INLINE std::string Validator::operator()(const std::string &str) const {
51 std::string value = str;
52 return (active_) ? func_(value) : std::string{};
53}
54
55CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const {
56 Validator newval(*this);
57 newval.desc_function_ = [validator_desc]() { return validator_desc; };
58 return newval;
59}
60
61CLI11_NODISCARD CLI11_INLINE std::string Validator::get_description() const {
62 if(active_) {
63 return desc_function_();
64 }
65 return std::string{};
66}
67
68CLI11_NODISCARD CLI11_INLINE Validator Validator::name(std::string validator_name) const {
69 Validator newval(*this);
70 newval.name_ = std::move(validator_name);
71 return newval;
72}
73
74CLI11_NODISCARD CLI11_INLINE Validator Validator::active(bool active_val) const {
75 Validator newval(*this);
76 newval.active_ = active_val;
77 return newval;
78}
79
80CLI11_NODISCARD CLI11_INLINE Validator Validator::application_index(int app_index) const {
81 Validator newval(*this);
82 newval.application_index_ = app_index;
83 return newval;
84}
85
86CLI11_INLINE Validator Validator::operator&(const Validator &other) const {
87 Validator newval;
88
89 newval._merge_description(*this, other, " AND ");
90
91 // Give references (will make a copy in lambda function)
92 const std::function<std::string(std::string & filename)> &f1 = func_;
93 const std::function<std::string(std::string & filename)> &f2 = other.func_;
94
95 newval.func_ = [f1, f2](std::string &input) {
96 std::string s1 = f1(input);
97 std::string s2 = f2(input);
98 if(!s1.empty() && !s2.empty())
99 return std::string("(") + s1 + ") AND (" + s2 + ")";
100 return s1 + s2;
101 };
102
103 newval.active_ = active_ && other.active_;
105 return newval;
106}
107
108CLI11_INLINE Validator Validator::operator|(const Validator &other) const {
109 Validator newval;
110
111 newval._merge_description(*this, other, " OR ");
112
113 // Give references (will make a copy in lambda function)
114 const std::function<std::string(std::string &)> &f1 = func_;
115 const std::function<std::string(std::string &)> &f2 = other.func_;
116
117 newval.func_ = [f1, f2](std::string &input) {
118 std::string s1;
119 std::string s2;
120 try {
121 s1 = f1(input);
122 } catch(const ValidationError &error) {
123 s1 = error.what();
124 if(s1.empty())
125 s1 = error.get_name();
126 }
127 try {
128 s2 = f2(input);
129 } catch(const ValidationError &error) {
130 s2 = error.what();
131 if(s2.empty())
132 s2 = error.get_name();
133 }
134 if(s1.empty() || s2.empty())
135 return std::string();
136
137 return std::string("(") + s1 + ") OR (" + s2 + ")";
138 };
139 newval.active_ = active_ && other.active_;
141 return newval;
142}
143
144CLI11_INLINE Validator Validator::operator!() const {
145 Validator newval;
146 const std::function<std::string()> &dfunc1 = desc_function_;
147 newval.desc_function_ = [dfunc1]() {
148 auto str = dfunc1();
149 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
150 };
151 // Give references (will make a copy in lambda function)
152 const std::function<std::string(std::string & res)> &f1 = func_;
153
154 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
155 std::string s1 = f1(test);
156 if(s1.empty()) {
157 return std::string("check ") + dfunc1() + " succeeded improperly";
158 }
159 return std::string{};
160 };
161 newval.active_ = active_;
163 return newval;
164}
165
166CLI11_INLINE void
167Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
168
169 const std::function<std::string()> &dfunc1 = val1.desc_function_;
170 const std::function<std::string()> &dfunc2 = val2.desc_function_;
171
172 desc_function_ = [=]() {
173 std::string f1 = dfunc1();
174 std::string f2 = dfunc2();
175 if((f1.empty()) || (f2.empty())) {
176 return f1 + f2;
177 }
178 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
179 };
180}
181
182namespace detail {
183
184#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
185CLI11_INLINE path_type check_path(const char *file) noexcept {
186 std::error_code ec;
187 auto stat = std::filesystem::status(to_path(file), ec);
188 if(ec) {
189 return path_type::nonexistent;
190 }
191 switch(stat.type()) {
192 case std::filesystem::file_type::none: // LCOV_EXCL_LINE
193 case std::filesystem::file_type::not_found:
194 return path_type::nonexistent; // LCOV_EXCL_LINE
195 case std::filesystem::file_type::directory:
196 return path_type::directory;
197 case std::filesystem::file_type::symlink:
198 case std::filesystem::file_type::block:
199 case std::filesystem::file_type::character:
200 case std::filesystem::file_type::fifo:
201 case std::filesystem::file_type::socket:
202 case std::filesystem::file_type::regular:
203 case std::filesystem::file_type::unknown:
204 default:
205 return path_type::file;
206 }
207}
208#else
209CLI11_INLINE path_type check_path(const char *file) noexcept {
210#if defined(_MSC_VER)
211 struct __stat64 buffer;
212 if(_stat64(file, &buffer) == 0) {
213 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
214 }
215#else
216 struct stat buffer;
217 if(stat(file, &buffer) == 0) {
218 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
219 }
220#endif
221 return path_type::nonexistent;
222}
223#endif
224
225CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") {
226 func_ = [](std::string &filename) -> std::string {
227 if(filename.empty())
228 return std::string("File name is missing.");
229
230 auto path_result = check_path(filename.c_str());
231 if(path_result == path_type::nonexistent) {
232 return "File does not exist: " + filename;
233 }
234 if(path_result == path_type::directory) {
235 return "File is actually a directory: " + filename;
236 }
237 return std::string();
238 };
239}
240
241CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") {
242 func_ = [](std::string &filename) {
243 auto path_result = check_path(filename.c_str());
244 if(path_result == path_type::nonexistent) {
245 return "Directory does not exist: " + filename;
246 }
247 if(path_result == path_type::file) {
248 return "Directory is actually a file: " + filename;
249 }
250 return std::string();
251 };
252}
253
254CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") {
255 func_ = [](std::string &filename) {
256 auto path_result = check_path(filename.c_str());
257 if(path_result == path_type::nonexistent) {
258 return "Path does not exist: " + filename;
259 }
260 return std::string();
261 };
262}
263
264CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") {
265 func_ = [](std::string &filename) {
266 auto path_result = check_path(filename.c_str());
267 if(path_result != path_type::nonexistent) {
268 return "Path already exists: " + filename;
269 }
270 return std::string();
271 };
272}
273
274CLI11_INLINE EscapedStringTransformer::EscapedStringTransformer() {
275 func_ = [](std::string &str) {
276 try {
277 if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') &&
278 str.front() == str.back()) {
279 process_quoted_string(str);
280 } else if(str.find_first_of('\\') != std::string::npos) {
281 if(detail::is_binary_escaped_string(str)) {
282 str = detail::extract_binary_string(str);
283 } else {
284 str = remove_escaped_characters(str);
285 }
286 }
287 return std::string{};
288 } catch(const std::invalid_argument &ia) {
289 return std::string(ia.what());
290 }
291 };
292}
293} // namespace detail
294
295CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn)
296 : Validator("FILE") {
297 func_ = [default_path, enableErrorReturn](std::string &filename) {
298 auto path_result = detail::check_path(filename.c_str());
299 if(path_result == detail::path_type::nonexistent) {
300 std::string test_file_path = default_path;
301 if(!default_path.empty() && default_path.back() != '/' && default_path.back() != '\\') {
302 // Add folder separator
303 test_file_path += '/';
304 }
305 test_file_path.append(filename);
306 path_result = detail::check_path(test_file_path.c_str());
307 if(path_result == detail::path_type::file) {
308 filename = test_file_path;
309 } else {
310 if(enableErrorReturn) {
311 return "File does not exist: " + filename;
312 }
313 }
314 }
315 return std::string{};
316 };
317}
318
319namespace detail {
320
321CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline) {
322 // try to determine the programName
323 std::pair<std::string, std::string> vals;
324 trim(commandline);
325 auto esp = commandline.find_first_of(' ', 1);
326 while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
327 if(esp == std::string::npos) {
328 // if we have reached the end and haven't found a valid file just assume the first argument is the
329 // program name
330 if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
331 bool embeddedQuote = false;
332 auto keyChar = commandline[0];
333 auto end = commandline.find_first_of(keyChar, 1);
334 while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes
335 end = commandline.find_first_of(keyChar, end + 1);
336 embeddedQuote = true;
337 }
338 if(end != std::string::npos) {
339 vals.first = commandline.substr(1, end - 1);
340 esp = end + 1;
341 if(embeddedQuote) {
342 vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
343 }
344 } else {
345 esp = commandline.find_first_of(' ', 1);
346 }
347 } else {
348 esp = commandline.find_first_of(' ', 1);
349 }
350
351 break;
352 }
353 esp = commandline.find_first_of(' ', esp + 1);
354 }
355 if(vals.first.empty()) {
356 vals.first = commandline.substr(0, esp);
357 rtrim(vals.first);
358 }
359
360 // strip the program name
361 vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{};
362 ltrim(vals.second);
363 return vals;
364}
365
366} // namespace detail
367
368// [CLI11:validators_inl_hpp:end]
369} // namespace CLI
Thrown when validation of results fails.
Definition Error.hpp:222
Some validators that are provided.
Definition Validators.hpp:55
Validator operator&(const Validator &other) const
Definition Validators_inl.hpp:86
int application_index_
A Validator will only apply to an indexed value (-1 is all elements).
Definition Validators.hpp:66
Validator & description(std::string validator_desc)
Specify the type string.
Definition Validators.hpp:96
Validator operator|(const Validator &other) const
Definition Validators_inl.hpp:108
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition Validators.hpp:68
bool non_modifying_
specify that a validator should not modify the input
Definition Validators.hpp:70
Validator & name(std::string validator_name)
Specify the type string.
Definition Validators.hpp:106
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition Validators.hpp:58
std::function< std::string(std::string &)> func_
Definition Validators.hpp:62
CLI11_NODISCARD std::string get_description() const
Generate type description information for the Validator.
Definition Validators_inl.hpp:61
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition Validators.hpp:115
std::string name_
The name for search purposes of the Validator.
Definition Validators.hpp:64
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition Validators_inl.hpp:144
std::string operator()(std::string &str) const
Definition Validators_inl.hpp:37
Validator & application_index(int app_index)
Specify the application index of a validator.
Definition Validators.hpp:128