You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 line
7.9KB

  1. #pragma once
  2. #include <libman/parse_fwd.hpp>
  3. #include <cassert>
  4. #include <filesystem>
  5. #include <optional>
  6. #include <string>
  7. #include <tuple>
  8. #include <utility>
  9. #include <vector>
  10. namespace lm {
  11. class pair {
  12. std::string _key;
  13. std::string _value;
  14. public:
  15. pair(std::string_view k, std::string_view v)
  16. : _key(k)
  17. , _value(v) {}
  18. auto& key() const noexcept { return _key; }
  19. auto& value() const noexcept { return _value; }
  20. template <std::size_t I>
  21. std::string_view get() const {
  22. if constexpr (I == 0) {
  23. return key();
  24. } else if constexpr (I == 1) {
  25. return value();
  26. }
  27. }
  28. };
  29. class pair_iterator {
  30. using vec_type = std::vector<pair>;
  31. using base_iter = vec_type::const_iterator;
  32. base_iter _iter;
  33. base_iter _end;
  34. std::string _key;
  35. public:
  36. using iterator_category = std::forward_iterator_tag;
  37. using difference_type = vec_type::difference_type;
  38. using value_type = vec_type::value_type;
  39. using pointer = vec_type::pointer;
  40. using reference = vec_type::reference;
  41. inline pair_iterator(base_iter, base_iter, std::string_view k);
  42. pair_iterator& operator++() & noexcept {
  43. assert(_iter != _end);
  44. ++_iter;
  45. while (_iter != _end && _iter->key() != _key) {
  46. ++_iter;
  47. }
  48. return *this;
  49. }
  50. const pair* operator->() const noexcept {
  51. assert(_iter != _end);
  52. return _iter.operator->();
  53. }
  54. const pair& operator*() const noexcept {
  55. assert(_iter != _end);
  56. return *_iter;
  57. }
  58. inline bool operator!=(const pair_iterator& o) const noexcept { return _iter != o._iter; }
  59. auto begin() const noexcept { return *this; }
  60. auto end() const noexcept { return pair_iterator(_end, _end, _key); }
  61. explicit operator bool() const noexcept { return *this != end(); }
  62. };
  63. class pair_list {
  64. std::vector<pair> _kvs;
  65. public:
  66. explicit pair_list(std::vector<pair> kvs)
  67. : _kvs(kvs) {}
  68. auto& items() const noexcept { return _kvs; }
  69. const pair* find(const std::string_view& key) const noexcept {
  70. for (auto&& item : items()) {
  71. if (item.key() == key) {
  72. return &item;
  73. }
  74. }
  75. return nullptr;
  76. }
  77. pair_iterator iter(std::string_view key) const noexcept {
  78. auto iter = items().begin();
  79. const auto end = items().end();
  80. while (iter != end && iter->key() != key) {
  81. ++iter;
  82. }
  83. return pair_iterator{iter, end, key};
  84. }
  85. std::vector<pair> all_of(std::string_view key) const noexcept {
  86. auto iter = this->iter(key);
  87. return std::vector<pair>(iter, iter.end());
  88. }
  89. auto size() const noexcept { return _kvs.size(); }
  90. };
  91. inline pair_iterator::pair_iterator(base_iter it, base_iter end, std::string_view k)
  92. : _iter{it}
  93. , _end{end}
  94. , _key{k} {}
  95. pair_list parse_string(std::string_view);
  96. pair_list parse_file(std::filesystem::path);
  97. void write_pairs(std::filesystem::path, const std::vector<pair>&);
  98. inline void write_pairs(const std::filesystem::path& fpath, const pair_list& pairs) {
  99. write_pairs(fpath, pairs.items());
  100. }
  101. template <typename What>
  102. class read_required {
  103. std::string_view _key;
  104. What& _ref;
  105. bool _did_read = false;
  106. public:
  107. read_required(std::string_view key, What& ref)
  108. : _key(key)
  109. , _ref(ref) {}
  110. int operator()(std::string_view context, std::string_view key, std::string_view value) {
  111. if (key != _key) {
  112. return 0;
  113. }
  114. if (_did_read) {
  115. throw std::runtime_error(std::string(context) + ": Duplicated key '" + std::string(key)
  116. + "' is not allowed");
  117. }
  118. _did_read = true;
  119. _ref = What(value);
  120. return 1;
  121. }
  122. void validate(std::string_view context) const {
  123. if (!_did_read) {
  124. throw std::runtime_error(std::string(context) + ": Missing required key '"
  125. + std::string(_key) + "'");
  126. }
  127. }
  128. };
  129. template <typename T>
  130. class read_opt {
  131. std::string_view _key;
  132. T& _ref;
  133. bool _did_read = false;
  134. public:
  135. read_opt(std::string_view key, T& ref)
  136. : _key(key)
  137. , _ref(ref) {}
  138. int operator()(std::string_view context, std::string_view key, std::string_view value) {
  139. if (key != _key) {
  140. return 0;
  141. }
  142. if (_did_read) {
  143. throw std::runtime_error(std::string(context) + ": Duplicated key '" + std::string(key)
  144. + "' is not allowed.");
  145. }
  146. _ref = T(value);
  147. return 1;
  148. }
  149. };
  150. template <typename T>
  151. class read_bool {
  152. std::string_view _key;
  153. T& _ref;
  154. bool _did_read = false;
  155. public:
  156. read_bool(std::string_view key, T& ref)
  157. : _key(key)
  158. , _ref(ref) {}
  159. bool operator()(std::string_view context, std::string_view key, std::string_view value) {
  160. if (key != _key) {
  161. return false;
  162. }
  163. if (_did_read) {
  164. throw std::runtime_error(std::string(context) + ": Duplicate key '" + std::string(key)
  165. + "' is not allowed.");
  166. }
  167. if (value == "true" || value == "True") {
  168. _ref = true;
  169. } else if (value == "false" || value == "False") {
  170. _ref = false;
  171. } else {
  172. throw std::runtime_error(std::string(context) + ": Invalid value '" + std::string(value)
  173. + "' for key '" + std::string(key)
  174. + ".' Expected `true` or `false`.");
  175. }
  176. _did_read = true;
  177. return true;
  178. }
  179. };
  180. class read_check_eq {
  181. std::string_view _key;
  182. std::string_view _expect;
  183. public:
  184. read_check_eq(std::string_view key, std::string_view value)
  185. : _key(key)
  186. , _expect(value) {}
  187. int operator()(std::string_view context, std::string_view key, std::string_view value) const {
  188. if (key != _key) {
  189. return 0;
  190. }
  191. if (value != _expect) {
  192. throw std::runtime_error(std::string(context) + ": Expected key '" + std::string(key)
  193. + "' to have value '" + std::string(_expect) + "' (Got '"
  194. + std::string(value) + "')");
  195. }
  196. return 1;
  197. }
  198. };
  199. template <typename Container>
  200. class read_accumulate {
  201. std::string_view _key;
  202. Container& _items;
  203. public:
  204. read_accumulate(std::string_view key, Container& c)
  205. : _key(key)
  206. , _items(c) {}
  207. int operator()(std::string_view, std::string_view key, std::string_view value) const {
  208. if (key == _key) {
  209. _items.emplace_back(value);
  210. return 1;
  211. }
  212. return 0;
  213. }
  214. };
  215. class reject_unknown {
  216. public:
  217. int operator()(std::string_view context, std::string_view key, std::string_view) const {
  218. throw std::runtime_error(std::string(context) + ": Unknown key '" + std::string(key) + "'");
  219. }
  220. };
  221. template <typename T>
  222. auto validate_reader(T&& t, std::string_view context, int) -> decltype(t.validate(context)) {
  223. t.validate(context);
  224. }
  225. template <typename T>
  226. void validate_reader(T&&, std::string_view, ...) {}
  227. template <typename... Items>
  228. auto read(std::string_view context [[maybe_unused]], const pair_list& pairs, Items... is) {
  229. std::vector<pair> bad_pairs;
  230. for (auto [key, value] : pairs.items()) {
  231. auto did_read = (is(context, key, value) || ...);
  232. if (did_read) {
  233. bad_pairs.emplace_back(key, value);
  234. }
  235. }
  236. (validate_reader(is, context, 0), ...);
  237. return bad_pairs;
  238. }
  239. } // namespace lm
  240. namespace std {
  241. template <>
  242. struct tuple_size<lm::pair> : std::integral_constant<int, 2> {};
  243. template <std::size_t N>
  244. struct tuple_element<N, lm::pair> {
  245. using type = std::string_view;
  246. };
  247. } // namespace std