Skip to content

Commit c8dc5f6

Browse files
add permission validators as an Extra Validator (#1203)
an update of #250 Fixes #249 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ee326d6 commit c8dc5f6

File tree

7 files changed

+247
-24
lines changed

7 files changed

+247
-24
lines changed

README.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ set with a simple and intuitive interface.
3838
- [Option options](#option-options)
3939
- [Validators](#validators)
4040
- [Default Validators](#default-validators)
41-
- [Validatrs that may be disabled 🚧](#validatrs-that-may-be-disabled-)
41+
- [Validators that may be disabled 🚧](#validators-that-may-be-disabled-)
4242
- [Extra Validators 🚧](#extra-validators-)
43-
- [Validator Usage](#validator-usage)
44-
- [Transforming Validators](#transforming-validators)
45-
- [Validator operations](#validator-operations)
46-
- [Custom Validators](#custom-validators)
47-
- [Querying Validators](#querying-validators)
48-
- [Getting results](#getting-results)
43+
- [permission. Requires C++17.](#permission-requires-c17)
44+
- [Validator Usage](#validator-usage)
45+
- [Transforming Validators](#transforming-validators)
46+
- [Validator operations](#validator-operations)
47+
- [Custom Validators](#custom-validators)
48+
- [Querying Validators](#querying-validators)
49+
- [Getting results](#getting-results)
4950
- [Subcommands](#subcommands)
5051
- [Subcommand options](#subcommand-options)
5152
- [Callbacks](#callbacks)
@@ -575,7 +576,9 @@ they can be disabled by using
575576
576577
#### Default Validators
577578
578-
These validators are always available regardless of definitions
579+
These validators are always available regardless of definitions. These are used
580+
internally or are very commonly used, so will always remain available regardless
581+
of flags.
579582
580583
- `CLI::ExistingFile`: Requires that the file exists if given.
581584
- `CLI::ExistingDirectory`: Requires that the directory exists.
@@ -590,11 +593,14 @@ These validators are always available regardless of definitions
590593
- `CLI::NonNegativeNumber`: Requires the number be greater or equal to 0
591594
- `CLI::Number`: Requires the input be a number.
592595
593-
#### Validatrs that may be disabled 🚧
596+
#### Validators that may be disabled 🚧
594597
595598
Validators that may be disabled by setting `CLI11_DISABLE_EXTRA_VALIDATORS` to 1
596599
or enabled by setting `CLI11_ENABLE_EXTRA_VALIDATORS` to 1. By default they are
597-
enabled.
600+
enabled. In version 3.0 these will likely move to be disabled by default and be
601+
controlled solely by the `CLI11_ENABLE_EXTRA_VALIDATORS` option. These
602+
validators are less commonly used or are template heavy and require additional
603+
computation time that may not be valuable for some use cases.
598604
599605
- `CLI::IsMember(...)`: Require an option be a member of a given set. See
600606
[Transforming Validators](#transforming-validators) for more details.
@@ -627,6 +633,14 @@ enabled.
627633
New validators will go into code sections that must be explicitly enabled by
628634
setting `CLI11_ENABLE_EXTRA_VALIDATORS` to 1
629635
636+
- `CLI::ReadPermission`: Requires that the file or folder given exist and have
637+
read permission. Requires C++17.
638+
- `CLI::WritePermission`: Requires that the file or folder given exist and have
639+
write permission. Requires C++17.
640+
- `CLI::ExecPermission`: Requires that the file given exist and have execution
641+
permission. Requires C++17.
642+
-
643+
630644
#### Validator Usage
631645
632646
These Validators once enabled can be used by simply passing the name into the

azure-pipelines.yml

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ jobs:
3434
vmImage: "windows-2025"
3535
cli11.std: 17
3636
cli11.build_type: Debug
37-
cli11.options: -G "Visual Studio 17 2022" -A ARM64
37+
cli11.options:
38+
-G "Visual Studio 17 2022" -A ARM64
39+
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
3840
pool:
3941
vmImage: $(vmImage)
4042

@@ -52,12 +54,15 @@ jobs:
5254
macOS-15_23:
5355
vmImage: "macOS-15"
5456
cli11.std: 23
57+
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
5558
macOS-14_20:
5659
vmImage: "macOS-14"
5760
cli11.std: 20
61+
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
5862
macOS-13_17:
5963
vmImage: "macOS-13"
6064
cli11.std: 17
65+
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
6166
macOS-14_11:
6267
vmImage: "macOS-14"
6368
cli11.std: 11
@@ -68,10 +73,12 @@ jobs:
6873
Windows17:
6974
vmImage: "windows-2022"
7075
cli11.std: 17
76+
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
7177
Windows17PC:
7278
vmImage: "windows-2022"
7379
cli11.std: 17
7480
cli11.precompile: ON
81+
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
7582
Windows11:
7683
vmImage: "windows-2022"
7784
cli11.std: 11
@@ -87,7 +94,8 @@ jobs:
8794
Linux17nortti:
8895
vmImage: "ubuntu-latest"
8996
cli11.std: 17
90-
cli11.options: -DCMAKE_CXX_FLAGS="-fno-rtti"
97+
cli11.options:
98+
-DCMAKE_CXX_FLAGS="-fno-rtti" -DCLI11_ENABLE_EXTRA_VALIDATORS=1
9199
pool:
92100
vmImage: $(vmImage)
93101
steps:
@@ -126,7 +134,9 @@ jobs:
126134
gcc9:
127135
containerImage: gcc:9
128136
cli11.std: 17
129-
cli11.options: -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
137+
cli11.options:
138+
-DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
139+
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
130140
gcc11:
131141
containerImage: gcc:11
132142
cli11.std: 20
@@ -143,19 +153,24 @@ jobs:
143153
clang3.4:
144154
containerImage: silkeh/clang:3.4
145155
cli11.std: 11
146-
cli11.options: -DCLI11_WARNINGS_AS_ERRORS=OFF
156+
cli11.options:
157+
-DCLI11_WARNINGS_AS_ERRORS=OFF -DCLI11_DISABLE_EXTRA_VALIDATORS=1
147158
clang8:
148159
containerImage: silkeh/clang:8
149160
cli11.std: 14
150-
cli11.options: -DCLI11_FORCE_LIBCXX=ON
161+
cli11.options:
162+
-DCLI11_FORCE_LIBCXX=ON -DCLI11_DISABLE_EXTRA_VALIDATORS=1
151163
clang8_17:
152164
containerImage: silkeh/clang:8
153165
cli11.std: 17
154-
cli11.options: -DCLI11_FORCE_LIBCXX=ON
166+
cli11.options:
167+
-DCLI11_FORCE_LIBCXX=ON -DCLI11_ENABLE_EXTRA_VALIDATORS=1
155168
clang10_20:
156169
containerImage: silkeh/clang:10
157170
cli11.std: 20
158-
cli11.options: -DCLI11_FORCE_LIBCXX=ON -DCMAKE_CXX_FLAGS=-std=c++20
171+
cli11.options:
172+
-DCLI11_FORCE_LIBCXX=ON -DCMAKE_CXX_FLAGS=-std=c++20
173+
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
159174
container: $[ variables['containerImage'] ]
160175
steps:
161176
- template: .ci/azure-cmake.yml
@@ -172,19 +187,25 @@ jobs:
172187
gcc13_17:
173188
containerImage: gcc:13
174189
cli11.std: 17
175-
cli11.options: -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
190+
cli11.options:
191+
-DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
192+
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
176193
gcc12_20:
177194
containerImage: gcc:12
178195
cli11.std: 20
179-
cli11.options: -DCMAKE_CXX_FLAGS="-Wredundant-decls -Wconversion"
196+
cli11.options:
197+
-DCMAKE_CXX_FLAGS="-Wredundant-decls -Wconversion"
198+
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
180199
clang17_23:
181200
containerImage: silkeh/clang:17
182201
cli11.std: 23
183-
cli11.options: -DCMAKE_CXX_FLAGS=-std=c++23
202+
cli11.options:
203+
-DCMAKE_CXX_FLAGS=-std=c++23 -DCLI11_ENABLE_EXTRA_VALIDATORS=1
184204
clang20_26:
185205
containerImage: silkeh/clang:20
186206
cli11.std: 26
187-
cli11.options: -DCMAKE_CXX_FLAGS=-std=c++2c
207+
cli11.options:
208+
-DCMAKE_CXX_FLAGS=-std=c++2c -DCLI11_ENABLE_EXTRA_VALIDATORS=1
188209
container: $[ variables['containerImage'] ]
189210
steps:
190211
- template: .ci/azure-cmake-new.yml

include/CLI/ExtraValidators.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//
55
// SPDX-License-Identifier: BSD-3-Clause
66

7+
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
8+
79
#pragma once
810
#if (defined(CLI11_ENABLE_EXTRA_VALIDATORS) && CLI11_ENABLE_EXTRA_VALIDATORS == 1) || \
911
(!defined(CLI11_DISABLE_EXTRA_VALIDATORS) || CLI11_DISABLE_EXTRA_VALIDATORS == 0)
@@ -591,6 +593,24 @@ class AsSizeValue : public AsNumberWithUnit {
591593

592594
#if defined(CLI11_ENABLE_EXTRA_VALIDATORS) && CLI11_ENABLE_EXTRA_VALIDATORS != 0
593595
// new extra validators
596+
#if CLI11_HAS_FILESYSTEM
597+
namespace detail {
598+
enum class Permission : std::uint8_t { none = 0, read = 1, write = 2, exec = 4 };
599+
class PermissionValidator : public Validator {
600+
public:
601+
explicit PermissionValidator(Permission permission);
602+
};
603+
} // namespace detail
604+
605+
/// Check that the file exist and available for read
606+
const detail::PermissionValidator ReadPermissions(detail::Permission::read);
607+
608+
/// Check that the file exist and available for write
609+
const detail::PermissionValidator WritePermissions(detail::Permission::write);
610+
611+
/// Check that the file exist and available for write
612+
const detail::PermissionValidator ExecPermissions(detail::Permission::exec);
613+
#endif
594614

595615
#endif
596616
// [CLI11:extra_validators_hpp:end]

include/CLI/impl/ExtraValidators_inl.hpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
// [CLI11:public_includes:set]
2121
#include <algorithm>
22+
#include <fstream>
2223
#include <map>
2324
#include <string>
2425
#include <utility>
@@ -94,7 +95,63 @@ CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::get_mappi
9495
namespace detail {} // namespace detail
9596
/// @}
9697

97-
// [CLI11:extra_validators_inl_hpp:end]
98+
#if defined(CLI11_ENABLE_EXTRA_VALIDATORS) && CLI11_ENABLE_EXTRA_VALIDATORS != 0
99+
// new extra validators
100+
namespace detail {
101+
102+
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
103+
CLI11_INLINE PermissionValidator::PermissionValidator(Permission permission) {
104+
std::filesystem::perms permission_code = std::filesystem::perms::none;
105+
std::string permission_name;
106+
switch(permission) {
107+
case Permission::read:
108+
permission_code = std::filesystem::perms::owner_read | std::filesystem::perms::group_read |
109+
std::filesystem::perms::others_read;
110+
permission_name = "read";
111+
break;
112+
case Permission::write:
113+
permission_code = std::filesystem::perms::owner_write | std::filesystem::perms::group_write |
114+
std::filesystem::perms::others_write;
115+
permission_name = "write";
116+
break;
117+
case Permission::exec:
118+
permission_code = std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec |
119+
std::filesystem::perms::others_exec;
120+
permission_name = "exec";
121+
break;
122+
case Permission::none:
123+
default:
124+
permission_code = std::filesystem::perms::none;
125+
break;
126+
}
127+
func_ = [permission_code](std::string &path) {
128+
std::error_code ec;
129+
auto p = std::filesystem::path(path);
130+
if(!std::filesystem::exists(p, ec)) {
131+
return std::string("Path does not exist: ") + path;
132+
}
133+
if(ec) {
134+
return std::string("Error checking path: ") + ec.message(); // LCOV_EXCL_LINE
135+
}
136+
if(permission_code == std::filesystem::perms::none) {
137+
return std::string{};
138+
}
139+
auto perms = std::filesystem::status(p, ec).permissions();
140+
if(ec) {
141+
return std::string("Error checking path status: ") + ec.message(); // LCOV_EXCL_LINE
142+
}
143+
if((perms & permission_code) == std::filesystem::perms::none) {
144+
return std::string("Path does not have required permissions: ") + path;
145+
}
146+
return std::string{};
147+
};
148+
description("Path with " + permission_name + " permission");
149+
}
150+
#endif
151+
152+
} // namespace detail
153+
#endif
154+
// [CLI11:extra_validators_inl_hpp:end]
98155
} // namespace CLI
99156

100157
#endif

include/CLI/impl/Option_inl.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -640,8 +640,9 @@ CLI11_INLINE void Option::_reduce_results(results_t &out, const results_t &origi
640640
}
641641
if(original.size() > num_max) {
642642
if(original.size() == 2 && num_max == 1 && original[1] == "%%" && original[0] == "{}") {
643-
// this condition is a trap for the following empty indicator check on config files
644-
out = original;
643+
// this condition is a trap for the following empty indicator check on config files, it may not be used
644+
// anymore
645+
out = original; // LCOV_EXCL_LINE
645646
} else {
646647
throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
647648
}

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ endif()
1919

2020
if(CLI11_ENABLE_EXTRA_VALIDATORS)
2121
target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_ENABLE_EXTRA_VALIDATORS=1)
22+
elseif(CLI11_DISABLE_EXTRA_VALIDATORS)
23+
target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_DISABLE_EXTRA_VALIDATORS=1)
24+
elseif(DEFINED CLI11_ENABLE_EXTRA_VALIDATORS)
25+
target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_ENABLE_EXTRA_VALIDATORS=0)
2226
endif()
2327
# Allow IDE's to group targets into folders
2428
add_library(CLI11::CLI11 ALIAS CLI11) # for add_subdirectory calls

0 commit comments

Comments
 (0)