$darkmode
number_impl.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  */
27 #pragma once
28 
29 #include <initializer_list> // for initializer_list<>
30 #include <limits> // for numeric_limits<>
31 #include <set> // for set<>
32 #include <string> // for string
33 #include <type_traits> // for enable_if_t<>, is_arithmetic<>
34 #include <utility> // for move
35 #include <vector> // for vector<>
36 
37 #include <fable/schema/number.hpp> // for Number<>
38 #include <fable/utility/templates.hpp> // for is_safe_cast<>, typeinfo<>
39 
40 namespace fable::schema {
41 
42 template <typename T>
43 Number<T> Number<T>::minimum(T value) && {
44  set_minimum(value);
45  return std::move(*this);
46 }
47 
48 template <typename T>
49 void Number<T>::set_minimum(T value) {
50  value_min_ = value;
51  exclusive_min_ = false;
52 }
53 
54 template <typename T>
55 Number<T> Number<T>::exclusive_minimum(T value) && {
56  set_exclusive_minimum(value);
57  return std::move(*this);
58 }
59 
60 template <typename T>
61 void Number<T>::set_exclusive_minimum(T value) {
62  value_min_ = value;
63  exclusive_min_ = true;
64 }
65 
66 template <typename T>
67 Number<T> Number<T>::maximum(T value) && {
68  set_maximum(value);
69  return std::move(*this);
70 }
71 
72 template <typename T>
73 void Number<T>::set_maximum(T value) {
74  value_max_ = value;
75  exclusive_max_ = false;
76 }
77 
78 template <typename T>
79 Number<T> Number<T>::exclusive_maximum(T value) && {
80  set_exclusive_maximum(value);
81  return std::move(*this);
82 }
83 
84 template <typename T>
85 void Number<T>::set_exclusive_maximum(T value) {
86  value_max_ = value;
87  exclusive_max_ = true;
88 }
89 
90 template <typename T>
91 std::pair<T, T> Number<T>::bounds() const {
92  return std::make_pair(value_min_, value_max_);
93 }
94 
95 template <typename T>
96 Number<T> Number<T>::bounds(T min, T max) && {
97  set_bounds(min, max);
98  return std::move(*this);
99 }
100 
101 template <typename T>
102 void Number<T>::set_bounds(T min, T max) {
103  exclusive_min_ = false;
104  value_min_ = min;
105  exclusive_max_ = false;
106  value_max_ = max;
107 }
108 
109 template <typename T>
110 Number<T> Number<T>::bounds_with(T min, T max, std::initializer_list<T> whitelisted) && {
111  exclusive_min_ = false;
112  value_min_ = min;
113  exclusive_max_ = false;
114  value_max_ = max;
115  for (auto x : whitelisted) {
116  insert_whitelist(x);
117  }
118  return std::move(*this);
119 }
120 
121 template <typename T>
122 Number<T> Number<T>::whitelist(T x) && {
123  insert_whitelist(x);
124  return std::move(*this);
125 }
126 
127 template <typename T>
128 Number<T> Number<T>::whitelist(std::initializer_list<T> xs) && {
129  extend_whitelist(std::move(xs));
130  return std::move(*this);
131 }
132 
133 template <typename T>
134 void Number<T>::insert_whitelist(T x) {
135  if (std::is_floating_point_v<T>) {
136  throw std::logic_error("cannot whitelist floating-point numbers");
137  }
138  if (blacklist_.count(x)) {
139  throw std::logic_error("cannot add blacklisted value to whitelist: " + std::to_string(x));
140  }
141  whitelist_.insert(x);
142 }
143 
144 template <typename T>
145 void Number<T>::extend_whitelist(std::initializer_list<T> xs) {
146  for (const auto& x : xs) {
147  insert_whitelist(x);
148  }
149 }
150 
151 template <typename T>
152 void Number<T>::reset_whitelist(std::initializer_list<T> xs) {
153  whitelist_.clear();
154  extend_whitelist(std::move(xs));
155 }
156 
157 template <typename T>
158 Number<T> Number<T>::blacklist(T x) && {
159  insert_blacklist(x);
160  return std::move(*this);
161 }
162 
163 template <typename T>
164 Number<T> Number<T>::blacklist(std::initializer_list<T> xs) && {
165  extend_blacklist(std::move(xs));
166  return std::move(*this);
167 }
168 
169 template <typename T>
170 void Number<T>::insert_blacklist(T x) {
171  if (std::is_floating_point_v<T>) {
172  throw std::logic_error("cannot blacklist floating-point numbers");
173  }
174  if (blacklist_.count(x)) {
175  throw std::logic_error("cannot add whitelisted value to blacklist: " + std::to_string(x));
176  }
177  blacklist_.insert(x);
178 }
179 
180 template <typename T>
181 void Number<T>::extend_blacklist(std::initializer_list<T> xs) {
182  for (const auto& x : xs) {
183  insert_blacklist(x);
184  }
185 }
186 
187 template <typename T>
188 void Number<T>::reset_blacklist(std::initializer_list<T> xs) {
189  blacklist_.clear();
190  extend_blacklist(std::move(xs));
191 }
192 
193 template <typename T>
195  Json j{
196  {"type", this->type_string()},
197  {exclusive_min_ ? "exclusiveMinimum" : "minimum", value_min_},
198  {exclusive_max_ ? "exclusiveMaximum" : "maximum", value_max_},
199  };
200 
201  if (!std::is_floating_point_v<T>) {
202  auto write_list = [&j](auto name, auto xlist) {
203  if (!xlist.empty()) {
204  std::vector<T> xs;
205  for (auto x : xlist) {
206  xs.emplace_back(x);
207  }
208  j[name] = xs;
209  }
210  };
211 
212  write_list("whitelist", whitelist_);
213  write_list("blacklist", blacklist_);
214  }
215 
216  this->augment_schema(j);
217  return j;
218 }
219 
220 template <typename T>
221 bool Number<T>::validate(const Conf& c, std::optional<SchemaError>& err) const {
222  switch (c->type()) {
223  case JsonType::number_unsigned:
224  return validate_bounds<uint64_t>(c, err);
225  case JsonType::number_integer:
226  return validate_bounds<int64_t>(c, err);
227  case JsonType::number_float:
228  if (this->type() != JsonType::number_float) {
229  return this->set_wrong_type(err, c);
230  } else {
231  return validate_bounds<double>(c, err);
232  }
233  default:
234  return this->set_wrong_type(err, c);
235  }
236 }
237 
238 template <typename T>
239 void Number<T>::to_json(Json& j) const {
240  assert(ptr_ != nullptr);
241  j = serialize(*ptr_);
242 }
243 
244 template <typename T>
245 void Number<T>::from_conf(const Conf& c) {
246  assert(ptr_ != nullptr);
247  *ptr_ = deserialize(c);
248 }
249 
250 template <typename T>
251 Json Number<T>::serialize(const T& x) const {
252  return x;
253 }
254 
255 template <typename T>
256 T Number<T>::deserialize(const Conf& c) const {
257  return c.get<T>();
258 }
259 
260 template <typename T>
261 void Number<T>::serialize_into(Json& j, const T& x) const {
262  j = x;
263 }
264 
265 template <typename T>
266 void Number<T>::deserialize_into(const Conf& c, T& x) const {
267  x = c.get<T>();
268 }
269 
270 template <typename T>
272  ptr_ = nullptr;
273 }
274 
288 template <typename T>
289 template <typename B>
290 bool Number<T>::validate_bounds(const Conf& c, std::optional<SchemaError>& err) const {
291  auto original = c.get<B>();
292  auto value = static_cast<T>(original);
293  if constexpr (!std::is_floating_point_v<T>) {
294  if (!is_cast_safe<T>(original)) {
295  return this->set_error(err, c,
296  "failed to convert input to destination type {}, got {}( {} ) = {}",
297  typeinfo<T>::name, typeinfo<B>::name, original, value);
298  }
299  }
300 
301  // Check whitelist and blacklist first:
302  if (!std::is_floating_point_v<T>) {
303  if (whitelist_.count(value)) {
304  return true;
305  }
306  if (blacklist_.count(value)) {
307  return this->set_error(err, c, "unexpected blacklisted value {}", value);
308  }
309  }
310 
311  // Check minimum value:
312  if (exclusive_min_) {
313  if (value <= value_min_) {
314  return this->set_error(err, c, "expected exclusive minimum > {}, got {}", value_min_, value);
315  }
316  } else {
317  if (value < value_min_) {
318  return this->set_error(err, c, "expected minimum >= {}, got {}", value_min_, value);
319  }
320  }
321 
322  if (exclusive_max_) {
323  if (value >= value_max_) {
324  return this->set_error(err, c, "expected exclusive maximum < {}, got {}", value_max_, value);
325  }
326  } else {
327  if (value > value_max_) {
328  return this->set_error(err, c, "expected maximum <= {}, got {}", value_max_, value);
329  }
330  }
331 
332  return true;
333 }
334 
335 } // namespace fable::schema
Definition: conf.hpp:81
T get() const
Definition: conf.hpp:297
Definition: number.hpp:36
void from_conf(const Conf &c) override
Definition: number_impl.hpp:245
virtual Json to_json() const
Definition: interface.hpp:254
Json json_schema() const override
Definition: number_impl.hpp:194
void reset_ptr() override
Definition: number_impl.hpp:271
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: number_impl.hpp:221
nlohmann::json Json
Definition: fable_fwd.hpp:35
Definition: templates.hpp:66