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.

263 line
6.8KB

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