Coreutils++
The GNU Coreutils implemented in C++
Loading...
Searching...
No Matches
ArgumentParser.hpp
Go to the documentation of this file.
1
20
21#ifndef LIB_DETAIL_ARGUMENTPARSER_HPP_
22#define LIB_DETAIL_ARGUMENTPARSER_HPP_
23
24#include <algorithm>
25#include <array>
26#include <concepts>
27#include <cstddef>
28#include <cstdint>
29#include <cstdlib>
30#include <format>
31#include <functional>
32#include <stdexcept>
33#include <string_view>
34#include <type_traits>
35#include <vector>
36
38
39template <std::size_t Length>
40struct ComptimeString final {
44 consteval ComptimeString(const char (&name)[Length]) {
45 // don't include null terminator. That's C crap.
46 std::copy(name, name + Length - 1, name_.begin());
47 }
48
49 constexpr bool operator==(const ComptimeString&) const = default;
50 constexpr auto operator<=>(const ComptimeString&) const = default;
51
52 consteval std::string_view PrintableView() const {
53 return std::string_view{name_.begin(), name_.size()};
54 }
55
56 std::array<char, Length - 1> name_;
57};
58
64enum class ParseState : std::uint8_t {
68};
69
70enum struct NArgs : std::uint8_t {
71 None, // e.g. --verbose
72 One, // e.g. --directory foo/bar
73 Many, // e.g. --names bob sally mary ...
74};
75
76template <ComptimeString PrimaryName, ComptimeString... Aliases>
78 static_assert(PrimaryName.PrintableView().starts_with("-"),
79 "Primary Argument name must start with a -");
80
81 static inline constexpr std::array<std::string_view, sizeof...(Aliases) + 1>
82 names_{PrimaryName.PrintableView(), Aliases.PrintableView()...};
83
84 static inline constexpr std::string_view help_view_{
85 PrimaryName.PrintableView()};
86
87 protected:
89};
90
91template <>
92struct ArgumentBase<""> {
93 static inline constexpr std::array<std::string_view, 0> names_{};
94
95 static inline constexpr std::string_view help_view_{};
96
97 protected:
99};
100
101template <class ParseType, NArgs N, auto Converter, ComptimeString... Names>
102 requires std::regular_invocable<decltype(Converter), std::string_view>
103struct Argument final {
104 static_assert(false, "Use a specialized version of this struct");
105};
106
109template <class T, auto Converter, ComptimeString... Names>
110 requires std::regular_invocable<decltype(Converter), std::string_view>
112 static_assert(!std::is_same_v<void, T>,
113 "Flag arguments cannot be of type void");
114
115 constexpr void TryParseValue(std::string_view arg) {
118 case ParseState::End:
119 // ignore
120 break;
122 value.emplace_back(std::invoke(Converter, arg));
123 break;
124 }
125 }
126 constexpr void TryParseFlag(std::string_view arg) {
127 bool is_this{std::ranges::any_of(
129 [arg](std::string_view name) { return name == arg; })};
130
133 if (is_this) {
135 }
136 break;
138 if (is_this) {
139 throw std::runtime_error{
140 std::format("ERROR! Duplicate option: {}", arg)};
141 } else if (!value.size()) {
142 throw std::runtime_error{std::format(
143 "ERROR! Do not specify {} and supply no arguments",
144 arg)};
145 } else {
147 }
148 break;
149 case ParseState::End:
150 if (is_this) {
151 throw std::runtime_error{
152 std::format("ERROR! Duplicate option: {}", arg)};
153 }
154 break;
155 }
156 }
157
158 // TODO(SEP): maybe take a template-template parameter to not force vector?
159 std::vector<T> value{};
160};
161
162template <class T, auto Converter>
163 requires std::regular_invocable<decltype(Converter), std::string_view>
165 static_assert(!std::is_same_v<void, T>,
166 "Positional arguments cannot be of type void");
167
168 constexpr void TryParseValue(std::string_view arg) {
169 switch (this->state_) {
172 // NOTE: fallthrough
174 value.emplace_back(std::invoke(Converter, arg));
175 break;
176 case ParseState::End:
177 break;
178 }
179 }
180 constexpr void TryParseFlag(std::string_view _) {
182 }
183
184 // TODO(SEP): maybe take a template-template parameter to not force vector?
185 std::vector<T> value{};
186};
187
188template <class T, auto Converter, ComptimeString... Names>
189struct Argument<T, NArgs::None, Converter, Names...> : ArgumentBase<Names...> {
190 static_assert(std::is_same_v<void, T>,
191 "A flag returning no values cannot have a non-void type");
192
193 constexpr void TryParseValue(std::string_view _) {}
194 constexpr void TryParseFlag(std::string_view arg) {
195 bool is_this{std::ranges::any_of(
197 [arg](std::string_view name) { return name == arg; })};
198
199 if (is_this) {
200 switch (this->state_) {
202 value = true;
203 this->state_ = ParseState::End;
204 break;
206 case ParseState::End:
207 throw std::runtime_error{
208 std::format("ERROR! Duplicate option: {}", arg)};
209 break;
210 }
211 }
212 }
213
214 bool value{};
215};
216
217template <class T, auto Converter, ComptimeString... Names>
218 requires std::regular_invocable<decltype(Converter), std::string_view>
220 static_assert(!std::is_same_v<void, T>,
221 "Flag argument cannot be of type void");
222
223 constexpr void TryParseValue(std::string_view arg) {
224 switch (this->state_) {
226 case ParseState::End:
227 break;
229 value = std::invoke(Converter, arg);
231 break;
232 }
233 }
234 constexpr void TryParseFlag(std::string_view arg) {
235 bool is_this{std::ranges::any_of(
237 [arg](std::string_view name) { return name == arg; })};
238
241 if (is_this) {
243 }
244 break;
246 case ParseState::End:
247 if (is_this) {
248 throw std::runtime_error{std::format(
249 "ERROR: unexpected repeated flag: {}", arg)};
250 }
251 break;
252 }
253 }
254
255 T value{};
256};
257} // namespace coreutils::detail
258
259#endif // LIB_DETAIL_ARGUMENTPARSER_HPP_
Definition ArgumentParser.hpp:37
NArgs
Definition ArgumentParser.hpp:70
@ One
Definition ArgumentParser.hpp:72
@ Many
Definition ArgumentParser.hpp:73
@ None
Definition ArgumentParser.hpp:71
ParseState
Definition ArgumentParser.hpp:64
@ End
Definition ArgumentParser.hpp:67
@ Start
Definition ArgumentParser.hpp:65
@ Seeking
Definition ArgumentParser.hpp:66
static constexpr std::string_view help_view_
Definition ArgumentParser.hpp:95
static constexpr std::array< std::string_view, 0 > names_
Definition ArgumentParser.hpp:93
ParseState state_
Definition ArgumentParser.hpp:98
Definition ArgumentParser.hpp:77
static constexpr std::array< std::string_view, sizeof...(Aliases)+1 > names_
Definition ArgumentParser.hpp:82
static constexpr std::string_view help_view_
Definition ArgumentParser.hpp:84
ParseState state_
Definition ArgumentParser.hpp:88
std::vector< T > value
Definition ArgumentParser.hpp:159
constexpr void TryParseValue(std::string_view arg)
Definition ArgumentParser.hpp:115
constexpr void TryParseFlag(std::string_view arg)
Definition ArgumentParser.hpp:126
constexpr void TryParseValue(std::string_view arg)
Definition ArgumentParser.hpp:168
std::vector< T > value
Definition ArgumentParser.hpp:185
constexpr void TryParseFlag(std::string_view _)
Definition ArgumentParser.hpp:180
constexpr void TryParseFlag(std::string_view arg)
Definition ArgumentParser.hpp:194
constexpr void TryParseValue(std::string_view _)
Definition ArgumentParser.hpp:193
constexpr void TryParseFlag(std::string_view arg)
Definition ArgumentParser.hpp:234
constexpr void TryParseValue(std::string_view arg)
Definition ArgumentParser.hpp:223
Definition ArgumentParser.hpp:103
Definition ArgumentParser.hpp:40
constexpr bool operator==(const ComptimeString &) const =default
std::array< char, Length - 1 > name_
Definition ArgumentParser.hpp:56
consteval ComptimeString(const char(&name)[Length])
Definition ArgumentParser.hpp:44
constexpr auto operator<=>(const ComptimeString &) const =default
consteval std::string_view PrintableView() const
Definition ArgumentParser.hpp:52