$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  value_min_ = value;
45  exclusive_min_ = false;
46  return std::move(*this);
47 }
48 
49 template <typename T>
50 Number<T> Number<T>::exclusive_minimum(T value) && {
51  value_min_ = value;
52  exclusive_min_ = true;
53  return std::move(*this);
54 }
55 
56 template <typename T>
57 Number<T> Number<T>::maximum(T value) && {
58  value_max_ = value;
59  exclusive_max_ = false;
60  return std::move(*this);
61 }
62 
63 template <typename T>
64 Number<T> Number<T>::exclusive_maximum(T value) && {
65  value_max_ = value;
66  exclusive_max_ = true;
67  return std::move(*this);
68 }
69 
70 template <typename T>
71 std::pair<T, T> Number<T>::bounds() const {
72  return std::make_pair(value_min_, value_max_);
73 }
74 
75 template <typename T>
76 Number<T> Number<T>::bounds(T min, T max) && {
77  exclusive_min_ = false;
78  value_min_ = min;
79  exclusive_max_ = false;
80  value_max_ = max;
81  return std::move(*this);
82 }
83 
84 template <typename T>
85 Number<T> Number<T>::bounds_with(T min, T max, std::initializer_list<T> whitelisted) && {
86  exclusive_min_ = false;
87  value_min_ = min;
88  exclusive_max_ = false;
89  value_max_ = max;
90  for (auto x : whitelisted) {
91  insert_whitelist(x);
92  }
93  return std::move(*this);
94 }
95 
96 template <typename T>
97 Number<T> Number<T>::whitelist(T x) && {
98  insert_whitelist(x);
99  return std::move(*this);
100 }
101 
102 template <typename T>
103 Number<T> Number<T>::whitelist(std::initializer_list<T> xs) && {
104  for (auto x : xs) {
105  insert_whitelist(x);
106  }
107  return std::move(*this);
108 }
109 
110 template <typename T>
111 void Number<T>::insert_whitelist(T x) {
112  if (std::is_floating_point_v<T>) {
113  throw std::logic_error("cannot whitelist floating-point numbers");
114  }
115  if (blacklist_.count(x)) {
116  throw std::logic_error("cannot add blacklisted value to whitelist: " + std::to_string(x));
117  }
118  whitelist_.insert(x);
119 }
120 
121 template <typename T>
122 Number<T> Number<T>::blacklist(T x) && {
123  insert_blacklist(x);
124  return std::move(*this);
125 }
126 
127 template <typename T>
128 Number<T> Number<T>::blacklist(std::initializer_list<T> xs) && {
129  for (auto x : xs) {
130  insert_blacklist(x);
131  }
132  return std::move(*this);
133 }
134 
135 template <typename T>
136 void Number<T>::insert_blacklist(T x) {
137  if (std::is_floating_point_v<T>) {
138  throw std::logic_error("cannot blacklist floating-point numbers");
139  }
140  if (blacklist_.count(x)) {
141  throw std::logic_error("cannot add whitelisted value to blacklist: " + std::to_string(x));
142  }
143  blacklist_.insert(x);
144 }
145 
146 template <typename T>
148  Json j{
149  {"type", this->type_string()},
150  {exclusive_min_ ? "exclusiveMinimum" : "minimum", value_min_},
151  {exclusive_max_ ? "exclusiveMaximum" : "maximum", value_max_},
152  };
153 
154  if (!std::is_floating_point_v<T>) {
155  auto write_list = [&j](auto name, auto xlist) {
156  if (!xlist.empty()) {
157  std::vector<T> xs;
158  for (auto x : xlist) {
159  xs.emplace_back(x);
160  }
161  j[name] = xs;
162  }
163  };
164 
165  write_list("whitelist", whitelist_);
166  write_list("blacklist", blacklist_);
167  }
168 
169  this->augment_schema(j);
170  return j;
171 }
172 
173 template <typename T>
174 bool Number<T>::validate(const Conf& c, std::optional<SchemaError>& err) const {
175  switch (c->type()) {
176  case JsonType::number_unsigned:
177  return validate_bounds<uint64_t>(c, err);
178  case JsonType::number_integer:
179  return validate_bounds<int64_t>(c, err);
180  case JsonType::number_float:
181  if (this->type() != JsonType::number_float) {
182  return this->set_wrong_type(err, c);
183  } else {
184  return validate_bounds<double>(c, err);
185  }
186  default:
187  return this->set_wrong_type(err, c);
188  }
189 }
190 
191 template <typename T>
192 void Number<T>::to_json(Json& j) const {
193  assert(ptr_ != nullptr);
194  j = serialize(*ptr_);
195 }
196 
197 template <typename T>
198 void Number<T>::from_conf(const Conf& c) {
199  assert(ptr_ != nullptr);
200  *ptr_ = deserialize(c);
201 }
202 
203 template <typename T>
204 Json Number<T>::serialize(const T& x) const {
205  return x;
206 }
207 
208 template <typename T>
209 T Number<T>::deserialize(const Conf& c) const {
210  return c.get<T>();
211 }
212 
213 template <typename T>
214 void Number<T>::serialize_into(Json& j, const T& x) const {
215  j = x;
216 }
217 
218 template <typename T>
219 void Number<T>::deserialize_into(const Conf& c, T& x) const {
220  x = c.get<T>();
221 }
222 
223 template <typename T>
225  ptr_ = nullptr;
226 }
227 
241 template <typename T>
242 template <typename B>
243 bool Number<T>::validate_bounds(const Conf& c, std::optional<SchemaError>& err) const {
244  auto original = c.get<B>();
245  auto value = static_cast<T>(original);
246  if constexpr (!std::is_floating_point_v<T>) {
247  if (!is_cast_safe<T>(original)) {
248  return this->set_error(err, c,
249  "failed to convert input to destination type {}, got {}( {} ) = {}",
250  typeinfo<T>::name, typeinfo<B>::name, original, value);
251  }
252  }
253 
254  // Check whitelist and blacklist first:
255  if (!std::is_floating_point_v<T>) {
256  if (whitelist_.count(value)) {
257  return true;
258  }
259  if (blacklist_.count(value)) {
260  return this->set_error(err, c, "unexpected blacklisted value {}", value);
261  }
262  }
263 
264  // Check minimum value:
265  if (exclusive_min_) {
266  if (value <= value_min_) {
267  return this->set_error(err, c, "expected exclusive minimum of {}, got {}", value_min_, value);
268  }
269  } else {
270  if (value < value_min_) {
271  return this->set_error(err, c, "expected minimum of {}, got {}", value_min_, value);
272  }
273  }
274 
275  if (exclusive_max_) {
276  if (value >= value_max_) {
277  return this->set_error(err, c, "expected exclusive maximum of {}, got {}", value_max_, value);
278  }
279  } else {
280  if (value > value_max_) {
281  return this->set_error(err, c, "expected maximum of {}, got {}", value_max_, value);
282  }
283  }
284 
285  return true;
286 }
287 
288 } // namespace fable::schema
Definition: conf.hpp:76
T get() const
Definition: conf.hpp:166
Definition: number.hpp:36
void from_conf(const Conf &c) override
Definition: number_impl.hpp:198
virtual Json to_json() const
Definition: interface.hpp:254
Json json_schema() const override
Definition: number_impl.hpp:147
void reset_ptr() override
Definition: number_impl.hpp:224
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: number_impl.hpp:174
nlohmann::json Json
Definition: fable_fwd.hpp:35
Definition: templates.hpp:66