Decentralised Art Server
High-performance C++ backend that exposes HTML interface and a secure REST API for managing Performative Transactions entities
 
Loading...
Searching...
No Matches
route_arg.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <format>
5#include <cassert>
6#include <tuple>
7#include <vector>
8#include <list>
9#include <memory>
10#include <type_traits>
11
12#include <spdlog/spdlog.h>
13
14#include "parse_error.hpp"
15
16namespace dcn::server
17{
23 enum class RouteArgType
24 {
25 // Unknown
26 Unknown = 0,
27
30 base58,
31 string,
32 array,
33 object
34 };
35
42 {
43 // Unknown
44 Unknown = 0,
45
48 };
49
57 {
60
63
64 // Copy constructor (deep copy)
67 {
68 children.reserve(other.children.size());
69 for (const auto& child : other.children)
70 {
71 if (child)
72 children.emplace_back(std::make_unique<RouteArgDef>(*child));
73 else
74 children.emplace_back(nullptr);
75 }
76 }
77
78 // Move constructor
80 : type(std::move(other.type)), requirement(std::move(other.requirement)), children(std::move(other.children)) {}
81
84 std::vector<std::unique_ptr<RouteArgDef>> children;
85 };
86
94 {
95 public:
96 RouteArg(RouteArgDef def, std::string data);
97
103 RouteArgType getType() const;
104
110 const std::string & getData() const;
111
118
124 const std::vector<std::unique_ptr<RouteArgDef>> & getChildren() const;
125
126 private:
127
128 RouteArgDef _def;
129 std::string _data;
130 };
131}
132
133namespace dcn::parse
134{
135 constexpr static const char ARRAY_START_IDENTIFIER = '[';
136 constexpr static const char ARRAY_END_IDENTIFIER = ']';
137
138 constexpr static const unsigned int MAX_OBJECT_FIELDS = 5;
139 constexpr static const char OBJECT_START_IDENTIFIER = '(';
140 constexpr static const char OBJECT_END_IDENTIFIER = ')';
141 constexpr static const char OBJECT_FIELDS_DELIMETER = ';';
142
143 // Base check for presence of value_type and iterator
144 template<typename T>
145 concept HasValueTypeAndIterator = requires {
146 typename T::value_type;
147 typename T::iterator;
148 };
149
150 // General concept for STL-like containers
151 template<typename T>
153 (
154 std::is_array<T>::value ||
155 std::is_same_v<T, std::vector<typename T::value_type>> ||
156 std::is_same_v<T, std::list<typename T::value_type>>
157 );
158
159 template<typename T>
160 struct is_tuple_like_impl : std::false_type {};
161
162 template<typename... Ts>
163 struct is_tuple_like_impl<std::tuple<Ts...>> : std::true_type {};
164
165 template<typename T1, typename T2>
166 struct is_tuple_like_impl<std::pair<T1, T2>> : std::true_type {};
167
168 template<typename T>
169 concept IsTupleLike =
170 is_tuple_like_impl<std::remove_cv_t<std::remove_reference_t<T>>>::value
171 && std::tuple_size<T>::value > 0
172 && std::tuple_size<T>::value < MAX_OBJECT_FIELDS;
173
183 server::RouteArgType parseRouteArgTypeFromString(const std::string & str);
184
193 parse::Result<server::RouteArgDef> parseRouteArgDefFromString(const std::string str);
194
195 template<class T>
197 parse::Result<T> parseRouteArgAs(const server::RouteArg & arg);
198
206 template<>
207 parse::Result<std::size_t> parseRouteArgAs<std::size_t>(const server::RouteArg & arg);
208
216 template<>
217 parse::Result<std::uint32_t> parseRouteArgAs<std::uint32_t>(const server::RouteArg & arg);
218
226 template<>
227 parse::Result<std::string> parseRouteArgAs<std::string>(const server::RouteArg & arg);
228
229
230 template <IsTupleLike TupleT, std::size_t Size>
231 struct TupleParser;
232
233 template <IsTupleLike TupleT>
234 struct TupleParser<TupleT, 2>
235 {
236 parse::Result<TupleT> operator()(const std::vector<std::unique_ptr<dcn::server::RouteArgDef>> &defs, const std::vector<std::string> & values_str)
237 {
238 if(values_str.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
239 if(defs.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
240
241 auto t0 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(0), values_str.at(0)));
242 if(!t0) return std::unexpected(t0.error());
243
244 auto t1 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(1), values_str.at(1)));
245 if(!t1) return std::unexpected(t1.error());
246
247 return std::make_tuple(*t0, *t1);
248 }
249 };
250
251 template <IsTupleLike TupleT>
252 struct TupleParser<TupleT, 3>
253 {
254 parse::Result<TupleT> operator()(const std::vector<std::unique_ptr<dcn::server::RouteArgDef>> &defs, const std::vector<std::string> & values_str)
255 {
256 if(values_str.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
257 if(defs.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
258
259 auto t0 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(0), values_str.at(0)));
260 if(!t0) return std::unexpected(t0.error());
261
262 auto t1 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(1), values_str.at(1)));
263 if(!t1) return std::unexpected(t1.error());
264
265 auto t2 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(2), values_str.at(2)));
266 if(!t2) return std::unexpected(t2.error());
267
268 return std::make_tuple(*t0, *t1, *t2);
269 }
270 };
271
272 template <IsTupleLike TupleT>
273 struct TupleParser<TupleT, 4>
274 {
275 parse::Result<TupleT> operator()(const std::vector<std::unique_ptr<dcn::server::RouteArgDef>> &defs, const std::vector<std::string> & values_str)
276 {
277 if(values_str.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
278 if(defs.size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
279
280 auto t0 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(0), values_str.at(0)));
281 if(!t0) return std::unexpected(t0.error());
282
283 auto t1 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(1), values_str.at(1)));
284 if(!t1) return std::unexpected(t1.error());
285
286 auto t2 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(2), values_str.at(2)));
287 if(!t2) return std::unexpected(t2.error());
288
289 auto t3 = parseRouteArgAs<typename std::tuple_element<0, TupleT>::type>(server::RouteArg(*defs.at(3), values_str.at(3)));
290 if(!t3) return std::unexpected(t3.error());
291
292 return std::make_tuple(*t0, *t1, *t2, *t3);
293 }
294 };
295
296
297 template<IsTupleLike TupleT>
298 parse::Result<TupleT> parseRouteArgAs(const server::RouteArg& arg)
299 {
300 if(arg.getType() != server::RouteArgType::object) return std::unexpected(ParseError{ParseError::Kind::TYPE_MISMATCH});
301 if(std::tuple_size<TupleT>::value == 0) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
302 if(arg.getChildren().size() != std::tuple_size<TupleT>::value) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
303
304 std::string data = arg.getData();
305 if(data.empty()) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
306 if(data.front() != OBJECT_START_IDENTIFIER) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
307 if(data.back() != OBJECT_END_IDENTIFIER) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
308
309 // remove delimiters
310 data = data.substr(1, data.size() - 2);
311
312 // split arg into fields
313 std::vector<std::string> values_str;
314 std::size_t begin = 0;
315 std::size_t end = 0;
316
317 while ((end = data.find(OBJECT_FIELDS_DELIMETER, begin)) != std::string::npos) {
318 values_str.push_back(data.substr(begin, (end - begin)));
319 begin = end + 1;
320 }
321 // add the last value
322 values_str.push_back(data.substr(begin));
323
324 return TupleParser<TupleT, std::tuple_size<TupleT>::value>{}(arg.getChildren(), values_str);
325 }
326
327 template<IsSequenceContainer ContainerT>
328 parse::Result<ContainerT> parseRouteArgAs(const server::RouteArg& arg)
329 {
330 using T = typename ContainerT::value_type;
331
332 if(arg.getType() != server::RouteArgType::array) return std::unexpected(ParseError{ParseError::Kind::TYPE_MISMATCH});
333 if(arg.getChildren().size() != 1) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
334 if(arg.getChildren().at(0) == nullptr) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
335
336 std::string data = arg.getData();
337 if(data.empty()) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
338 if(data.front() != ARRAY_START_IDENTIFIER) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
339 if(data.back() != ARRAY_END_IDENTIFIER) return std::unexpected(ParseError{ParseError::Kind::INVALID_VALUE});
340
341 // remove delimiters
342 data = data.substr(1, data.size() - 2);
343
344 // split arg into values
345 constexpr static const char array_values_delimeter = ',';
346
347 std::vector<std::string> values_str;
348 std::size_t begin = 0;
349 std::size_t end = 0;
350
351 while ((end = data.find(array_values_delimeter, begin)) != std::string::npos) {
352 values_str.push_back(data.substr(begin, (end - begin)));
353 begin = end + 1;
354 }
355 // add the last value
356 values_str.push_back(data.substr(begin));
357
358 ContainerT values;
359 const auto & array_type = *arg.getChildren().at(0);
360
361 for (const auto & value_str : values_str)
362 {
363 server::RouteArg array_value = server::RouteArg(array_type, value_str);
364 const auto parsed_value = parseRouteArgAs<T>(array_value);
365 if(!parsed_value)return std::unexpected(parsed_value.error());
366
367 values.emplace_back(*parsed_value);
368 }
369
370 return values;
371 }
372}
373
374template <>
375struct std::formatter<dcn::server::RouteArgType> : std::formatter<std::string> {
376 auto format(const dcn::server::RouteArgType & arg_type, format_context& ctx) const {
377 switch(arg_type)
378 {
379 case dcn::server::RouteArgType::character: return formatter<string>::format("char", ctx);
380 case dcn::server::RouteArgType::unsigned_integer: return formatter<string>::format("uint", ctx);
381 case dcn::server::RouteArgType::string: return formatter<string>::format("string", ctx);
382 case dcn::server::RouteArgType::base58: return formatter<string>::format("base58", ctx);
383 case dcn::server::RouteArgType::array: return formatter<string>::format("array", ctx);
384 case dcn::server::RouteArgType::object: return formatter<string>::format("object", ctx);
385
386 // Unknown
387 case dcn::server::RouteArgType::Unknown: return formatter<string>::format("Unknown", ctx);
388 }
389 return formatter<string>::format("", ctx);
390 }
391};
392
393template <>
394struct std::formatter<dcn::server::RouteArgRequirement> : std::formatter<std::string> {
395 auto format(const dcn::server::RouteArgRequirement & req, format_context& ctx) const {
396 switch(req)
397 {
398 case dcn::server::RouteArgRequirement::required: return formatter<string>::format("required", ctx);
399 case dcn::server::RouteArgRequirement::optional: return formatter<string>::format("(optional)", ctx);
400
401 // Unknown
402 case dcn::server::RouteArgRequirement::Unknown: return formatter<string>::format("Unknown", ctx);
403 }
404 return formatter<string>::format("", ctx);
405 }
406};
407
408template <>
409struct std::formatter<dcn::server::RouteArg> : std::formatter<std::string> {
410 auto format(const dcn::server::RouteArg & arg, format_context& ctx) const {
411 return formatter<string>::format(
412 std::format("({}) [{}] {}", arg.getRequirement(), arg.getType(), arg.getData()), ctx);
413 }
414};
A class representing a route argument.
Definition route_arg.hpp:94
const std::vector< std::unique_ptr< RouteArgDef > > & getChildren() const
Gets the children of the route argument.
Definition route_arg.cpp:27
RouteArgRequirement getRequirement() const
Gets the requirement of the route argument.
Definition route_arg.cpp:22
RouteArgType getType() const
Gets the type of the route argument.
Definition route_arg.cpp:12
const std::string & getData() const
Gets the data associated with the route argument.
Definition route_arg.cpp:17
Definition route_arg.hpp:145
Definition route_arg.hpp:152
Definition route_arg.hpp:169
Definition auth.cpp:4
Definition route.hpp:22
RouteArgRequirement
Enum to represent the requirement of a route argument.
Definition route_arg.hpp:42
RouteArgType
Enum to represent the type of a route argument.
Definition route_arg.hpp:24
Definition decentralised_art.hpp:30
Definition parse_error.hpp:11
A pair of RouteArgType and RouteArgRequirement.
Definition route_arg.hpp:57
RouteArgDef(RouteArgDef &&other) noexcept
Definition route_arg.hpp:79
RouteArgDef(RouteArgType type, RouteArgRequirement requirement)
Definition route_arg.hpp:58
std::vector< std::unique_ptr< RouteArgDef > > children
Definition route_arg.hpp:84
RouteArgType type
Definition route_arg.hpp:82
RouteArgDef(const RouteArgDef &other)
Definition route_arg.hpp:65
RouteArgDef(RouteArgType type, RouteArgRequirement requirement, std::vector< std::unique_ptr< RouteArgDef > > children)
Definition route_arg.hpp:61
RouteArgRequirement requirement
Definition route_arg.hpp:83
auto format(const dcn::server::RouteArgRequirement &req, format_context &ctx) const
Definition route_arg.hpp:395
auto format(const dcn::server::RouteArgType &arg_type, format_context &ctx) const
Definition route_arg.hpp:376
auto format(const dcn::server::RouteArg &arg, format_context &ctx) const
Definition route_arg.hpp:410