11#ifndef INP_DECKFIELD_H
12#define INP_DECKFIELD_H
43template <
class Deck>
struct Field {
55 std::function<void(Deck &,
const json &)>
read;
64 std::function<void(
const Deck &, std::ostringstream &,
const std::string &)>
75template <
class T>
inline std::string
typeName() {
return "value"; }
87template <>
inline std::string typeName<std::vector<double>>() {
91template <>
inline std::string typeName<std::vector<std::string>>() {
95template <>
inline std::string typeName<std::vector<std::size_t>>() {
107void printValue(std::ostringstream &oss,
const std::vector<T> &v) {
108 oss << util::io::printStr<T>(v, 0);
124 std::optional<T>
min = std::nullopt;
127 std::optional<T>
max = std::nullopt;
143template <
class Deck,
class T>
145 T Deck::*m, std::string key, T def, std::string doc,
147 std::function<bool(
const T &)> emitIf = [](
const T &) {
return true; }) {
151 constexpr bool checkable =
152 std::is_arithmetic_v<T> || std::is_same_v<T, std::string>;
154 auto check = [key, accepts](
const T &v) {
155 if constexpr (checkable) {
156 if (!accepts.values.empty() &&
157 std::find(accepts.values.begin(), accepts.values.end(), v) ==
158 accepts.values.end()) {
159 std::ostringstream oss;
160 oss <<
"Error: " << key <<
" must be one of:";
161 for (
const auto &a : accepts.values)
163 oss <<
". Given value is " << v <<
".\n";
164 throw std::runtime_error(oss.str());
166 if (accepts.min.has_value() && v < accepts.min.value()) {
167 std::ostringstream oss;
168 oss <<
"Error: " << key <<
" must be at least " << accepts.min.value()
169 <<
". Given value is " << v <<
".\n";
170 throw std::runtime_error(oss.str());
172 if (accepts.max.has_value() && accepts.max.value() < v) {
173 std::ostringstream oss;
174 oss <<
"Error: " << key <<
" must be at most " << accepts.max.value()
175 <<
". Given value is " << v <<
".\n";
176 throw std::runtime_error(oss.str());
182 key, std::move(doc), typeName<T>(),
183 [m, key, def,
check](Deck &d,
const json &j) {
184 d.*m = j.value(key, def);
187 [m, key, emitIf](
const Deck &d,
json &j) {
191 [key, def, emitIf](
json &j) {
195 [m, key](
const Deck &d, std::ostringstream &oss,
196 const std::string &tab) {
197 oss << tab << key <<
" = ";
202 [emitIf](
const json &v) {
return emitIf(v.get<T>()); }};
214template <
class Deck,
class T>
217 auto f =
field(m, key, T{}, std::move(doc), accepts);
218 f.
read = [m, key, accepts, f](Deck &d,
const json &j) {
219 if (j.find(key) == j.end())
220 throw std::runtime_error(
"Error: " + key +
" is required.\n");
221 json one = json::object();
222 one[key] = j.at(key);
231 for (
const auto &f : fs)
238 for (
const auto &f : fs)
253 const std::string &tab =
"") {
254 for (
const auto &f : fs)
255 f.print(d, oss, tab);
261 json j = json::object();
262 for (
const auto &f : fs)
277 json out = json::array();
279 for (
const auto &f : fs) {
280 json entry = {{
"key", f.key}, {
"type", f.type}, {
"doc", f.doc}};
281 if (defaults.find(f.key) != defaults.end())
282 entry[
"default"] = defaults.at(f.key);
283 out.push_back(entry);
318 for (
const auto &g : groups) {
319 std::vector<std::string> present;
320 for (
const auto &k : g.keys)
321 if (block.find(k) != block.end())
322 present.push_back(k);
324 if (present.size() == 1 || (present.empty() && !g.required))
327 std::ostringstream oss;
328 oss <<
"Error: give exactly one of";
329 for (
const auto &k : g.keys)
331 oss <<
" for " << g.what <<
". ";
333 oss <<
"None was given.\n";
335 oss <<
"These were given:";
336 for (
const auto &k : present)
340 throw std::runtime_error(oss.str());
354inline std::size_t
editDistance(
const std::string &a,
const std::string &b) {
355 std::vector<std::size_t> prev(b.size() + 1), cur(b.size() + 1);
356 for (std::size_t j = 0; j <= b.size(); ++j)
358 for (std::size_t i = 1; i <= a.size(); ++i) {
360 for (std::size_t j = 1; j <= b.size(); ++j)
361 cur[j] = std::min({prev[j] + 1, cur[j - 1] + 1,
362 prev[j - 1] + (a[i - 1] == b[j - 1] ? 0 : 1)});
365 return prev[b.size()];
380 const std::vector<std::string> &names) {
381 if (given.is_null() || given.empty())
383 if (!given.is_object())
384 throw std::runtime_error(
385 "Error: deck values must be given as name and value pairs.\n");
387 for (
const auto &item : given.items()) {
388 const std::string key = item.key();
389 if (std::find(names.begin(), names.end(), key) != names.end())
393 std::size_t best = std::string::npos;
394 for (
const auto &n : names) {
401 std::ostringstream oss;
402 oss <<
"Error: no field named " << key <<
".";
404 oss <<
" Closest declared field is " << closest <<
".";
405 oss <<
"\nDeclared fields are:";
406 for (
const auto &n : names)
409 throw std::runtime_error(oss.str());
422 const std::vector<std::string> &extra = {}) {
423 std::vector<std::string> names;
424 names.reserve(fs.size() + extra.size());
425 for (
const auto &f : fs)
426 names.push_back(f.key);
427 names.insert(names.end(), extra.begin(), extra.end());
444 const std::vector<std::string> &extra = {}) {
447 if (!given.is_object())
450 for (
const auto &item : given.items()) {
452 for (
const auto &f : fs)
453 if (f.key == item.key()) {
454 f.check(item.value());
455 emit = f.emit(item.value());
461 j[item.key()] = item.value();
nlohmann::ordered_json json
void check(bool ok, const std::string &what)
Collection of methods and database related to input.
std::size_t editDistance(const std::string &a, const std::string &b)
Number of single character edits between two strings.
std::string typeName< bool >()
Name of the type of a field, reported by the schema.
Field< Deck > field(T Deck::*m, std::string key, T def, std::string doc, Accepts< T > accepts={}, std::function< bool(const T &)> emitIf=[](const T &) { return true;})
Declares a field that maps one key to one member.
void readFields(Deck &d, const json &j, const std::vector< Field< Deck > > &fs)
Reads every field of the table from the block.
json defaultsJson(const std::vector< Field< Deck > > &fs)
The block with every field at its default.
std::string typeName< std::size_t >()
Name of the type of a field, reported by the schema.
void printValue(std::ostringstream &oss, const T &v)
Appends a value to a stream.
void writeFields(const Deck &d, json &j, const std::vector< Field< Deck > > &fs)
Writes every field of the table into the block.
void checkNames(const json &given, const std::vector< std::string > &names)
Rejects a key that is not among those named.
void printFields(const Deck &d, std::ostringstream &oss, const std::vector< Field< Deck > > &fs, const std::string &tab="")
Appends every field of the table to a stream.
std::string typeName< double >()
Name of the type of a field, reported by the schema.
json applyGiven(const json &given, const std::vector< Field< Deck > > &fs, const std::vector< std::string > &extra={})
The default block with the given keys replaced.
void checkKeys(const json &given, const std::vector< Field< Deck > > &fs, const std::vector< std::string > &extra={})
Rejects a key that no field declares.
Field< Deck > requiredField(T Deck::*m, std::string key, std::string doc, Accepts< T > accepts={})
Declares a field that has no default and must be present.
void checkGroups(const json &block, const std::vector< OneOf > &groups)
Checks that each group has exactly one of its keys.
std::string typeName< std::string >()
Name of the type of a field, reported by the schema.
std::string typeName()
Name of the type of a field, reported by the schema.
json schemaJson(const std::vector< Field< Deck > > &fs)
Key, type, default and description of every field.
std::string typeName< int >()
Name of the type of a field, reported by the schema.
std::optional< T > max
Upper bound, applied when it is set.
std::vector< T > values
Accepted values, or empty when any value is accepted.
std::optional< T > min
Lower bound, applied when it is set.
One field of a deck, declared once.
std::function< bool(const json &)> emit
Whether a value is written rather than left out of the block.
std::string doc
One line describing the field, reported by the schema.
std::function< void(const json &)> check
Throws if the value is one the field does not accept.
std::string key
Key in the JSON block.
std::string type
Name of the type, reported by the schema.
std::function< void(Deck &, const json &)> read
Reads the key into the member, or leaves the default in place.
std::function< void(const Deck &, std::ostringstream &, const std::string &)> print
Appends the key and its value to a stream, after a tab prefix.
std::function< void(const Deck &, json &)> write
Writes the member under the key, if it is to be written.
std::function< void(json &)> writeDefault
Writes the default under the key, if it is to be written.
A quantity that can be given in more than one way.
bool required
Whether one of the keys has to be given.
std::string what
What the group gives, named in the error message.
std::vector< std::string > keys
Keys of the group.