poaflocParser Of Arguments For Lines Of Commands |
git clone git://git.dimitrijedobrota.com/poafloc.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
poafloc.cpp (6534B)
0 #include "poafloc/poafloc.hpp"
2 #include "poafloc/error.hpp"
4 namespace
5 {
7 constexpr bool is_option_str(std::string_view arg)
8 {
9 return arg.starts_with("-");
10 }
12 constexpr bool is_next_option(std::span<const std::string_view> args)
13 {
14 return args.empty() || is_option_str(args.front());
15 }
17 } // namespace
19 namespace poafloc::detail
20 {
22 option::option(option::type opt_type, func_type func, std::string_view help)
23 : m_type(opt_type)
24 , m_func(std::move(func))
25 , m_name(help)
26 {
27 }
29 option::option(
30 option::type opt_type,
31 std::string_view opts,
32 func_type func,
33 std::string_view help
34 )
35 : m_type(opt_type)
36 , m_func(std::move(func))
37 {
38 auto istr = std::istringstream(std::string(opts));
39 std::string str;
41 while (std::getline(istr, str, ' ')) {
42 if (std::size(str) == 1) {
43 m_opt_short = str[0];
44 } else {
45 m_opt_long = str;
46 }
47 }
49 if (!has_opt_short() && !has_opt_long()) {
50 throw error<error_code::missing_option>();
51 }
53 if (opt_type != option::type::boolean) {
54 const auto pos = help.find(' ');
55 m_name = help.substr(0, pos);
56 m_message = help.substr(pos + 1);
57 } else {
58 m_message = help;
59 }
60 }
62 void parser_base::process(const option& option)
63 {
64 if (option.has_opt_short()) {
65 const auto& opt_short = option.opt_short();
66 if (!m_opt_short.set(opt_short, std::size(m_options))) {
67 throw error<error_code::duplicate_option>(opt_short);
68 }
69 }
71 if (option.has_opt_long()) {
72 const auto& opt_long = option.opt_long();
73 if (!m_opt_long.set(opt_long, std::size(m_options))) {
74 throw error<error_code::duplicate_option>(opt_long);
75 }
76 }
78 m_options.emplace_back(option);
79 }
81 void parser_base::operator()(void* record, int argc, const char** argv)
82 {
83 std::vector<std::string_view> args(
84 argv, argv + argc // NOLINT(*pointer*)
85 );
86 operator()(record, args);
87 }
89 void parser_base::operator()(
90 void* record, std::span<const std::string_view> args
91 )
92 {
93 if (args.empty()) {
94 throw error<error_code::empty>();
95 }
97 const auto program = args[0];
98 std::size_t arg_idx = 1;
99 bool is_term = false;
101 while (arg_idx != std::size(args)) {
102 const auto arg_raw = args[arg_idx];
104 if (!is_option_str(arg_raw)) {
105 break;
106 }
108 if (std::size(arg_raw) == 1) {
109 throw error<error_code::unknown_option>("-");
110 }
112 if (arg_raw == "--") {
113 is_term = true;
114 ++arg_idx;
115 break;
116 }
118 const auto next = args.subspan(arg_idx + 1);
119 const auto res = arg_raw[1] != '-'
120 ? hdl_short_opts(program, record, arg_raw.substr(1), next)
121 : hdl_long_opt(program, record, arg_raw.substr(2), next);
122 arg_idx = std::size(args) - std::size(res);
123 }
125 size_type count = 0_u;
126 while (arg_idx != std::size(args)) {
127 const auto arg = args[arg_idx++];
128 if (!is_term && arg == "--") {
129 throw error<error_code::invalid_terminal>(arg);
130 }
132 if (!is_term && is_option_str(arg)) {
133 throw error<error_code::invalid_positional>(arg);
134 }
136 if (!m_pos.is_list() && count == std::size(m_pos)) {
137 throw error<error_code::superfluous_positional>(std::size(m_pos));
138 }
140 if (count == std::size(m_pos)) {
141 count--;
142 }
144 m_pos[count++](record, arg);
145 }
147 if (count < std::size(m_pos)) {
148 throw error<error_code::missing_positional>(std::size(m_pos));
149 }
150 }
152 parser_base::next_t parser_base::hdl_short_opt(
153 void* record, based::character opt, std::string_view rest, next_t next
154 ) const
155 {
156 const auto option = get_option(opt);
158 if (!rest.empty()) {
159 if (rest.front() != '=') {
160 option(record, rest);
161 return next;
162 }
164 const auto value = rest.substr(1);
165 if (!value.empty()) {
166 option(record, value);
167 return next;
168 }
170 throw error<error_code::missing_argument>(opt);
171 }
173 if (is_next_option(next)) {
174 throw error<error_code::missing_argument>(opt);
175 }
177 if (option.get_type() != option::type::list) {
178 option(record, next.front());
179 return next.subspan(1);
180 }
182 while (!is_next_option(next)) {
183 option(record, next.front());
184 next = next.subspan(1);
185 }
187 return next;
188 }
190 parser_base::next_t parser_base::hdl_short_opts(
191 std::string_view program, void* record, std::string_view arg, next_t next
192 ) const
193 {
194 std::size_t opt_idx = 0;
195 while (opt_idx < std::size(arg)) {
196 const auto opt = arg[opt_idx];
198 if (opt == '?') {
199 (void)help_long(program);
200 throw error<error_code::help>();
201 }
203 const auto option = get_option(opt);
204 if (option.get_type() != option::type::boolean) {
205 break;
206 }
208 option(record, program);
209 opt_idx++;
210 }
212 return opt_idx == std::size(arg)
213 ? next
214 : hdl_short_opt(record, arg[opt_idx], arg.substr(opt_idx + 1), next);
215 }
217 parser_base::next_t parser_base::hdl_long_opt(
218 std::string_view program, void* record, std::string_view arg, next_t next
219 ) const
220 {
221 const auto equal = arg.find('=');
222 if (equal != std::string::npos) {
223 auto opt = arg.substr(0, equal);
224 const auto value = arg.substr(equal + 1);
226 const auto option = get_option(opt);
227 if (option.get_type() == option::type::boolean) {
228 throw error<error_code::superfluous_argument>(opt);
229 }
231 if (!value.empty()) {
232 option(record, value);
233 return next;
234 }
236 throw error<error_code::missing_argument>(opt);
237 }
239 const auto opt = arg;
241 if (opt == "help") {
242 (void)help_long(program);
243 throw error<error_code::help>();
244 }
246 if (opt == "usage") {
247 (void)help_short(program);
248 throw error<error_code::help>();
249 }
251 const auto option = get_option(opt);
252 if (option.get_type() == option::type::boolean) {
253 option(record, program);
254 return next;
255 }
257 if (is_next_option(next)) {
258 throw error<error_code::missing_argument>(opt);
259 }
261 if (option.get_type() != option::type::list) {
262 option(record, next.front());
263 return next.subspan(1);
264 }
266 while (!is_next_option(next)) {
267 option(record, next.front());
268 next = next.subspan(1);
269 }
271 return next;
272 }
274 [[nodiscard]] const option& parser_base::get_option(based::character opt) const
275 {
276 const auto idx = m_opt_short.get(opt);
277 if (!idx.has_value()) {
278 throw error<error_code::unknown_option>(opt);
279 }
280 return m_options[idx.value()];
281 }
283 [[nodiscard]] const option& parser_base::get_option(std::string_view opt) const
284 {
285 const auto idx = m_opt_long.get(opt);
286 if (!idx.has_value()) {
287 throw error<error_code::unknown_option>(opt);
288 }
289 return m_options[idx.value()];
290 }
292 } // namespace poafloc::detail