1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #pragma once
4
5 #include <algorithm>
6 #include <cstddef>
7 #include <functional>
8 #include <map>
9 #include <string>
10 #include <vector>
11
12 #include <cm/optional>
13 #include <cm/string_view>
14
15 #include <cm3p/json/value.h>
16
17 template <typename T, typename E>
18 using cmJSONHelper = std::function<E(T& out, const Json::Value* value)>;
19
20 template <typename T, typename E>
21 class cmJSONObjectHelper
22 {
23 public:
24 cmJSONObjectHelper(E&& success, E&& fail, bool allowExtra = true);
25
26 template <typename U, typename M, typename F>
27 cmJSONObjectHelper& Bind(const cm::string_view& name, M U::*member, F func,
28 bool required = true);
29 template <typename M, typename F>
30 cmJSONObjectHelper& Bind(const cm::string_view& name, std::nullptr_t, F func,
31 bool required = true);
32 template <typename F>
33 cmJSONObjectHelper& Bind(const cm::string_view& name, F func,
34 bool required = true);
35
36 E operator()(T& out, const Json::Value* value) const;
37
38 private:
39 // Not a true cmJSONHelper, it just happens to match the signature
40 using MemberFunction = std::function<E(T& out, const Json::Value* value)>;
41 struct Member
42 {
43 cm::string_view Name;
44 MemberFunction Function;
45 bool Required;
46 };
47 std::vector<Member> Members;
48 bool AnyRequired = false;
49 E Success;
50 E Fail;
51 bool AllowExtra;
52
53 cmJSONObjectHelper& BindPrivate(const cm::string_view& name,
54 MemberFunction&& func, bool required);
55 };
56
57 template <typename T, typename E>
cmJSONObjectHelper(E && success,E && fail,bool allowExtra)58 cmJSONObjectHelper<T, E>::cmJSONObjectHelper(E&& success, E&& fail,
59 bool allowExtra)
60 : Success(std::move(success))
61 , Fail(std::move(fail))
62 , AllowExtra(allowExtra)
63 {
64 }
65
66 template <typename T, typename E>
67 template <typename U, typename M, typename F>
Bind(const cm::string_view & name,M U::* member,F func,bool required)68 cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
69 const cm::string_view& name, M U::*member, F func, bool required)
70 {
71 return this->BindPrivate(
72 name,
73 [func, member](T& out, const Json::Value* value) -> E {
74 return func(out.*member, value);
75 },
76 required);
77 }
78
79 template <typename T, typename E>
80 template <typename M, typename F>
Bind(const cm::string_view & name,std::nullptr_t,F func,bool required)81 cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
82 const cm::string_view& name, std::nullptr_t, F func, bool required)
83 {
84 return this->BindPrivate(name,
85 [func](T& /*out*/, const Json::Value* value) -> E {
86 M dummy;
87 return func(dummy, value);
88 },
89 required);
90 }
91
92 template <typename T, typename E>
93 template <typename F>
Bind(const cm::string_view & name,F func,bool required)94 cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
95 const cm::string_view& name, F func, bool required)
96 {
97 return this->BindPrivate(name, MemberFunction(func), required);
98 }
99
100 template <typename T, typename E>
BindPrivate(const cm::string_view & name,MemberFunction && func,bool required)101 cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::BindPrivate(
102 const cm::string_view& name, MemberFunction&& func, bool required)
103 {
104 Member m;
105 m.Name = name;
106 m.Function = std::move(func);
107 m.Required = required;
108 this->Members.push_back(std::move(m));
109 if (required) {
110 this->AnyRequired = true;
111 }
112 return *this;
113 }
114
115 template <typename T, typename E>
operator()116 E cmJSONObjectHelper<T, E>::operator()(T& out, const Json::Value* value) const
117 {
118 if (!value && this->AnyRequired) {
119 return this->Fail;
120 }
121 if (value && !value->isObject()) {
122 return this->Fail;
123 }
124 Json::Value::Members extraFields;
125 if (value) {
126 extraFields = value->getMemberNames();
127 }
128
129 for (auto const& m : this->Members) {
130 std::string name(m.Name.data(), m.Name.size());
131 if (value && value->isMember(name)) {
132 E result = m.Function(out, &(*value)[name]);
133 if (result != this->Success) {
134 return result;
135 }
136 extraFields.erase(
137 std::find(extraFields.begin(), extraFields.end(), name));
138 } else if (!m.Required) {
139 E result = m.Function(out, nullptr);
140 if (result != this->Success) {
141 return result;
142 }
143 } else {
144 return this->Fail;
145 }
146 }
147
148 return this->AllowExtra || extraFields.empty() ? this->Success : this->Fail;
149 }
150
151 template <typename E>
152 cmJSONHelper<std::string, E> cmJSONStringHelper(E success, E fail,
153 const std::string& defval = "")
154 {
155 return
156 [success, fail, defval](std::string& out, const Json::Value* value) -> E {
157 if (!value) {
158 out = defval;
159 return success;
160 }
161 if (!value->isString()) {
162 return fail;
163 }
164 out = value->asString();
165 return success;
166 };
167 }
168
169 template <typename E>
170 cmJSONHelper<int, E> cmJSONIntHelper(E success, E fail, int defval = 0)
171 {
172 return [success, fail, defval](int& out, const Json::Value* value) -> E {
173 if (!value) {
174 out = defval;
175 return success;
176 }
177 if (!value->isInt()) {
178 return fail;
179 }
180 out = value->asInt();
181 return success;
182 };
183 }
184
185 template <typename E>
186 cmJSONHelper<unsigned int, E> cmJSONUIntHelper(E success, E fail,
187 unsigned int defval = 0)
188 {
189 return
190 [success, fail, defval](unsigned int& out, const Json::Value* value) -> E {
191 if (!value) {
192 out = defval;
193 return success;
194 }
195 if (!value->isUInt()) {
196 return fail;
197 }
198 out = value->asUInt();
199 return success;
200 };
201 }
202
203 template <typename E>
204 cmJSONHelper<bool, E> cmJSONBoolHelper(E success, E fail, bool defval = false)
205 {
206 return [success, fail, defval](bool& out, const Json::Value* value) -> E {
207 if (!value) {
208 out = defval;
209 return success;
210 }
211 if (!value->isBool()) {
212 return fail;
213 }
214 out = value->asBool();
215 return success;
216 };
217 }
218
219 template <typename T, typename E, typename F, typename Filter>
cmJSONVectorFilterHelper(E success,E fail,F func,Filter filter)220 cmJSONHelper<std::vector<T>, E> cmJSONVectorFilterHelper(E success, E fail,
221 F func, Filter filter)
222 {
223 return [success, fail, func, filter](std::vector<T>& out,
224 const Json::Value* value) -> E {
225 if (!value) {
226 out.clear();
227 return success;
228 }
229 if (!value->isArray()) {
230 return fail;
231 }
232 out.clear();
233 for (auto const& item : *value) {
234 T t;
235 E result = func(t, &item);
236 if (result != success) {
237 return result;
238 }
239 if (!filter(t)) {
240 continue;
241 }
242 out.push_back(std::move(t));
243 }
244 return success;
245 };
246 }
247
248 template <typename T, typename E, typename F>
cmJSONVectorHelper(E success,E fail,F func)249 cmJSONHelper<std::vector<T>, E> cmJSONVectorHelper(E success, E fail, F func)
250 {
251 return cmJSONVectorFilterHelper<T, E, F>(success, fail, func,
252 [](const T&) { return true; });
253 }
254
255 template <typename T, typename E, typename F, typename Filter>
cmJSONMapFilterHelper(E success,E fail,F func,Filter filter)256 cmJSONHelper<std::map<std::string, T>, E> cmJSONMapFilterHelper(E success,
257 E fail, F func,
258 Filter filter)
259 {
260 return [success, fail, func, filter](std::map<std::string, T>& out,
261 const Json::Value* value) -> E {
262 if (!value) {
263 out.clear();
264 return success;
265 }
266 if (!value->isObject()) {
267 return fail;
268 }
269 out.clear();
270 for (auto const& key : value->getMemberNames()) {
271 if (!filter(key)) {
272 continue;
273 }
274 T t;
275 E result = func(t, &(*value)[key]);
276 if (result != success) {
277 return result;
278 }
279 out[key] = std::move(t);
280 }
281 return success;
282 };
283 }
284
285 template <typename T, typename E, typename F>
cmJSONMapHelper(E success,E fail,F func)286 cmJSONHelper<std::map<std::string, T>, E> cmJSONMapHelper(E success, E fail,
287 F func)
288 {
289 return cmJSONMapFilterHelper<T, E, F>(
290 success, fail, func, [](const std::string&) { return true; });
291 }
292
293 template <typename T, typename E, typename F>
cmJSONOptionalHelper(E success,F func)294 cmJSONHelper<cm::optional<T>, E> cmJSONOptionalHelper(E success, F func)
295 {
296 return [success, func](cm::optional<T>& out, const Json::Value* value) -> E {
297 if (!value) {
298 out.reset();
299 return success;
300 }
301 out.emplace();
302 return func(*out, value);
303 };
304 }
305
306 template <typename T, typename E, typename F>
cmJSONRequiredHelper(E fail,F func)307 cmJSONHelper<T, E> cmJSONRequiredHelper(E fail, F func)
308 {
309 return [fail, func](T& out, const Json::Value* value) -> E {
310 if (!value) {
311 return fail;
312 }
313 return func(out, value);
314 };
315 }
316