HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
CommandLineParser.hpp
1// Copyright Take Vos 2019-2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "datum.hpp"
8#include <string>
9#include <vector>
10#include <map>
11#include <functional>
12#include <string_view>
13#include <iostream>
14
15namespace tt {
16
17using namespace std::literals;
18
24 struct option_t {
27 std::string name;
28
31 datum_type_t type;
32
35 std::string help;
36
40 std::function<int(std::string_view)> enum_conversion;
41
42 option_t(std::string name, datum_type_t type, std::string help, std::function<int(std::string_view)> enum_conversion) :
43 name(std::move(name)), type(type), help(std::move(help)), enum_conversion(std::move(enum_conversion)) {}
44 };
45
46 // The synopsis of the application to be printed on --help and error.
47 std::string synopsis;
48
49 // A list of options.
51
53 std::vector<std::string> error_messages;
54
55public:
56 CommandLineParser(std::string synopsis) : synopsis(std::move(synopsis)) {}
57
65 void add(std::string name, datum_type_t type, std::string help, std::function<int(std::string_view)> enum_conversion = {}) noexcept {
66 options.emplace_back(std::move(name), type, std::move(help), std::move(enum_conversion));
67 }
68
71 bool has_error() const noexcept {
72 return error_messages.size() > 0;
73 }
74
78 void print_help() {
79 for (ttlet &error_message: error_messages) {
80 std::cerr << error_message << "\n";
81 }
82 if (has_error()) {
83 std::cerr << "\n";
84 }
85
86 std::cerr << synopsis << "\n";
87
88 for (ttlet &option: options) {
89 ttlet example = fmt::format("--{}=<{}>", option.name, option.type);
90 std::cerr << fmt::format(" {:20s} {}\n", example, option.help);
91 }
92 std::cerr.flush();
93 }
94
104 datum parse(int argc, char const * const argv[]) noexcept {
105 auto r = datum{datum::map{}};
106
107 for (int arg_index = 0; arg_index != argc; ++arg_index) {
108 auto argument = std::string(argv[arg_index]);
109
110 if (arg_index == 0) {
111 r["executable-path"] = argument;
112
113 } else if (argument.starts_with("--"s)) {
114 ttlet i = argument.find('=');
115 if (i == argument.npos) {
116 ttlet option_name = argument.substr(2);
117
118 ttlet &option = std::find_if(options.begin(), options.end(), [&](auto x) {
119 return x.name == option_name;
120 });
121
122 if (option == options.end()) {
123 error_messages.push_back(fmt::format("Unknown option '{}'", option_name));
124
125 } else if (option->type != datum_type_t::Boolean) {
126 error_messages.push_back(fmt::format("Option '{}' requires an argument", option_name));
127
128 } else {
129 r[option_name] = true;
130 }
131
132 } else {
133 ttlet option_name = argument.substr(2, i-2);
134 ttlet option_value_string = argument.substr(i+1);
135
136 ttlet &option = std::find_if(options.begin(), options.end(), [&](auto x) {
137 return x.name == option_name;
138 });
139
140 if (option == options.end()) {
141 error_messages.push_back(fmt::format("Unknown option '{}'", option_name));
142
143 } else {
144 switch (option->type) {
145 case datum_type_t::Boolean:
146 if (option_value_string == "true") {
147 r[option_name] = true;
148 } else if (option_value_string == "false") {
149 r[option_name] = false;
150 } else {
151 error_messages.push_back(
152 fmt::format("Expected a boolean value ('true' or 'false') for option '{}' got '{}'", option_name, option_value_string)
153 );
154 }
155 break;
156
157 case datum_type_t::Integer:
158 if (option->enum_conversion) {
159 ttlet option_value_int = option->enum_conversion(option_value_string);
160 if (option_value_int >= 0) {
161 r[option_name] = option_value_int;
162 } else {
163 error_messages.push_back(
164 fmt::format("Unknown value '{}' for option '{}'", option_value_string, option_name)
165 );
166 }
167
168 } else {
169 try {
170 ttlet option_value_int = std::stoll(option_value_string);
171 r[option_name] = option_value_int;
172 } catch (...) {
173 error_messages.push_back(
174 fmt::format("Expected a integer value for option '{}' got '{}'", option_name, option_value_string)
175 );
176 }
177 }
178 break;
179
180 case datum_type_t::String:
181 r[option_name] = option_value_string;
182 break;
183
184 case datum_type_t::Vector:
185 r[option_name].push_back(datum{option_value_string});
186 break;
187
188 case datum_type_t::URL:
189 r[option_name] = URL::urlFromCurrentWorkingDirectory().urlByAppendingPath(option_value_string);
190 break;
191
192 default:
193 tt_no_default();
194 }
195 }
196 }
197
198 } else {
199 r["arguments"].push_back(argument);
200 }
201 }
202 return r;
203 }
204};
205
206}
A parser to parse command line arguments.
Definition CommandLineParser.hpp:21
bool has_error() const noexcept
check if an error has occured during parsing.
Definition CommandLineParser.hpp:71
datum parse(int argc, char const *const argv[]) noexcept
Parse the arguments.
Definition CommandLineParser.hpp:104
void add(std::string name, datum_type_t type, std::string help, std::function< int(std::string_view)> enum_conversion={}) noexcept
Add a configuration option.
Definition CommandLineParser.hpp:65
void print_help()
Print help text for the command line arguments.
Definition CommandLineParser.hpp:78
T begin(T... args)
T emplace_back(T... args)
T end(T... args)
T find_if(T... args)
T move(T... args)
T push_back(T... args)
T size(T... args)
T stoll(T... args)