$darkmode
map.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
24 #pragma once
25 #ifndef FABLE_SCHEMA_MAP_HPP_
26 #define FABLE_SCHEMA_MAP_HPP_
27 
28 #include <limits> // for numeric_limits<>
29 #include <map> // for map<>
30 #include <memory> // for shared_ptr<>
31 #include <regex> // for regex, regex_match
32 #include <string> // for string
33 #include <utility> // for move
34 #include <vector> // for vector<>
35 
36 #include <boost/optional.hpp> // for optional<>
37 
38 #include <fable/schema/interface.hpp> // for Base<>
39 
40 namespace fable {
41 namespace schema {
42 
51 template <typename T, typename P>
52 class Map : public Base<Map<T, P>> {
53  public: // Types and Constructors
54  using Type = std::map<std::string, T>;
55  using PrototypeSchema = P;
56 
57  Map(Type* ptr, std::string&& desc);
58  Map(Type* ptr, const PrototypeSchema& prototype)
59  : Base<Map<T, P>>(JsonType::object), prototype_(prototype), ptr_(ptr) {
60  prototype_.reset_ptr();
61  }
62  Map(Type* ptr, const PrototypeSchema& prototype, std::string&& desc)
63  : Base<Map<T, P>>(JsonType::object, std::move(desc)), prototype_(prototype), ptr_(ptr) {
64  prototype_.reset_ptr();
65  }
66 
67 #if 0
68  // This is defined in: fable/schema/magic.hpp
69  Map(Type* ptr, std::string&& desc)
70  : Map(ptr, make_prototype<T>(), std::move(desc)) {}
71 #endif
72 
73  public: // Special
74  bool unique_properties() const { return unique_properties_; }
75  Map<T, P> unique_properties(bool value) && {
76  unique_properties_ = value;
77  return std::move(*this);
78  }
79 
80  const std::vector<std::string>& required() const { return required_; }
81  Map<T, P> require_properties(const std::vector<std::string>& values) && {
82  required_ = values;
83  return std::move(*this);
84  }
85  Map<T, P> require_property(const std::string& value) && {
86  required_.emplace_back(value);
87  return std::move(*this);
88  }
89 
90  const std::string& pattern() const { return pattern_; }
91  Map<T, P> pattern(const std::string& value) && {
92  pattern_ = value;
93  return std::move(*this);
94  }
95 
96  public: // Overrides
97  Json json_schema() const override {
98  Json j{
99  {"type", "object"},
100  {"additionalProperties", prototype_.json_schema()},
101  };
102  if (!required_.empty()) {
103  j["required"] = required_;
104  }
105  if (min_properties_ != 0) {
106  j["minProperties"] = min_properties_;
107  }
108  if (max_properties_ != std::numeric_limits<size_t>::max()) {
109  j["maxProperties"] = max_properties_;
110  }
111  if (!pattern_.empty()) {
112  j["propertyNames"]["pattern"] = pattern_;
113  }
114  this->augment_schema(j);
115  return j;
116  }
117 
118  void validate(const Conf& c) const override {
119  this->validate_type(c);
120  if (c->size() < min_properties_) {
121  this->throw_error(c, "expect at least {} properties, got {}", max_properties_, c->size());
122  }
123  assert(required_.size() <= max_properties_);
124  if (c->size() > max_properties_) {
125  this->throw_error(c, "expect at most {} properties, got {}", max_properties_, c->size());
126  }
127  for (auto& k : required_) {
128  c.assert_has(k);
129  }
130 
131  boost::optional<std::regex> pattern;
132  if (!pattern_.empty()) {
133  *pattern = std::regex(pattern_);
134  }
135  for (const auto& kv : c->items()) {
136  prototype_.validate(c.at(kv.key()));
137  if (pattern && !std::regex_match(kv.key(), *pattern)) {
138  this->throw_error(c, "expect property name to match regex '{}': {}", pattern_, kv.key());
139  }
140  }
141  }
142 
143  using Interface::to_json;
144  void to_json(Json& j) const override {
145  assert(ptr_ != nullptr);
146  j = serialize(*ptr_);
147  }
148 
149  void from_conf(const Conf& c) override {
150  assert(ptr_ != nullptr);
151  for (auto& i : c->items()) {
152  const auto key = i.key();
153  if (unique_properties_ && ptr_->count(key)) {
154  this->throw_error(c, "key {} has already been defined", key);
155  }
156  ptr_->insert(std::make_pair(key, deserialize_item(c, key)));
157  }
158  }
159 
160  Json serialize(const Type& xm) const {
161  Json j;
162  for (const auto& kv : xm) {
163  j[kv.first] = prototype_.serialize(kv.second);
164  }
165  return j;
166  }
167 
168  Type deserialize(const Conf& c) const {
169  Type tmp;
170  for (auto& i : c->items()) {
171  const auto key = i.key();
172  tmp.insert(std::make_pair(key, deserialize_item(c, key)));
173  }
174  return tmp;
175  }
176 
177  T deserialize_item(const Conf& c, const std::string& key) const {
178  return prototype_.deserialize(c.at(key));
179  }
180 
181  void reset_ptr() override { ptr_ = nullptr; }
182 
183  private:
184  bool unique_properties_{true};
185  size_t min_properties_{0};
186  size_t max_properties_{std::numeric_limits<size_t>::max()};
187  std::string pattern_{};
188  std::vector<std::string> required_{};
189  PrototypeSchema prototype_{};
190  Type* ptr_{nullptr};
191 };
192 
193 template <typename T, typename P>
194 Map<T, P> make_schema(std::map<std::string, T>* ptr, const P& prototype, std::string&& desc) {
195  return Map<T, P>(ptr, prototype, std::move(desc));
196 }
197 
198 } // namespace schema
199 } // namespace fable
200 
201 #endif // FABLE_SCHEMA_MAP_HPP_
Definition: conf.hpp:74
void to_json(Json &j) const override
Definition: map.hpp:144
void from_conf(const Conf &c) override
Definition: map.hpp:149
void assert_has(const std::string &key) const
Definition: conf.cpp:109
Json json_schema() const override
Definition: map.hpp:97
nlohmann::json Json
Definition: json.hpp:62
Definition: conf.hpp:70
Definition: map.hpp:52
virtual Json to_json() const
Definition: interface.hpp:220
Conf at(const std::string &key) const
Definition: conf.cpp:58
void validate(const Conf &c) const override
Definition: map.hpp:118
Definition: interface.hpp:354
void reset_ptr() override
Definition: map.hpp:181