Validators.hpp
6.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include "CLI/TypeTools.hpp"
#include <functional>
#include <iostream>
#include <string>
// C standard library
// Only needed for existence checking
// Could be swapped for filesystem in C++17
#include <sys/stat.h>
#include <sys/types.h>
namespace CLI {
/// @defgroup validator_group Validators
/// @brief Some validators that are provided
///
/// These are simple `std::string(const std::string&)` validators that are useful. They return
/// a string if the validation fails. A custom struct is provided, as well, with the same user
/// semantics, but with the ability to provide a new type name.
/// @{
///
struct Validator {
/// This is the type name, if empty the type name will not be changed
std::string tname;
std::function<std::string(const std::string &filename)> func;
/// This is the required operator for a validator - provided to help
/// users (CLI11 uses the func directly)
std::string operator()(const std::string &filename) const { return func(filename); };
/// Combining validators is a new validator
Validator operator&(const Validator &other) const {
Validator newval;
newval.tname = (tname == other.tname ? tname : "");
// Give references (will make a copy in lambda function)
const std::function<std::string(const std::string &filename)> &f1 = func;
const std::function<std::string(const std::string &filename)> &f2 = other.func;
newval.func = [f1, f2](const std::string &filename) {
std::string s1 = f1(filename);
std::string s2 = f2(filename);
if(!s1.empty() && !s2.empty())
return s1 + " & " + s2;
else
return s1 + s2;
};
return newval;
}
/// Combining validators is a new validator
Validator operator|(const Validator &other) const {
Validator newval;
newval.tname = (tname == other.tname ? tname : "");
// Give references (will make a copy in lambda function)
const std::function<std::string(const std::string &filename)> &f1 = func;
const std::function<std::string(const std::string &filename)> &f2 = other.func;
newval.func = [f1, f2](const std::string &filename) {
std::string s1 = f1(filename);
std::string s2 = f2(filename);
if(s1.empty() || s2.empty())
return std::string();
else
return s1 + " & " + s2;
};
return newval;
}
};
// The implementation of the built in validators is using the Validator class;
// the user is only expected to use the const (static) versions (since there's no setup).
// Therefore, this is in detail.
namespace detail {
/// Check for an existing file (returns error message if check fails)
struct ExistingFileValidator : public Validator {
ExistingFileValidator() {
tname = "FILE";
func = [](const std::string &filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
return "File does not exist: " + filename;
} else if(is_dir) {
return "File is actually a directory: " + filename;
}
return std::string();
};
}
};
/// Check for an existing directory (returns error message if check fails)
struct ExistingDirectoryValidator : public Validator {
ExistingDirectoryValidator() {
tname = "DIR";
func = [](const std::string &filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
return "Directory does not exist: " + filename;
} else if(!is_dir) {
return "Directory is actually a file: " + filename;
}
return std::string();
};
}
};
/// Check for an existing path
struct ExistingPathValidator : public Validator {
ExistingPathValidator() {
tname = "PATH";
func = [](const std::string &filename) {
struct stat buffer;
bool const exist = stat(filename.c_str(), &buffer) == 0;
if(!exist) {
return "Path does not exist: " + filename;
}
return std::string();
};
}
};
/// Check for an non-existing path
struct NonexistentPathValidator : public Validator {
NonexistentPathValidator() {
tname = "PATH";
func = [](const std::string &filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
if(exist) {
return "Path already exists: " + filename;
}
return std::string();
};
}
};
} // namespace detail
// Static is not needed here, because global const implies static.
/// Check for existing file (returns error message if check fails)
const detail::ExistingFileValidator ExistingFile;
/// Check for an existing directory (returns error message if check fails)
const detail::ExistingDirectoryValidator ExistingDirectory;
/// Check for an existing path
const detail::ExistingPathValidator ExistingPath;
/// Check for an non-existing path
const detail::NonexistentPathValidator NonexistentPath;
/// Produce a range (factory). Min and max are inclusive.
struct Range : public Validator {
template <typename T> Range(T min, T max) {
std::stringstream out;
out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
tname = out.str();
func = [min, max](std::string input) {
T val;
detail::lexical_cast(input, val);
if(val < min || val > max)
return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max);
return std::string();
};
}
/// Range of one value is 0 to value
template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
};
/// @}
} // namespace CLI