rpc.hpp 0.8.1
Simple RPC Header-Only Library
rpc_njson.hpp
Go to the documentation of this file.
1
36
37#pragma once
38
39#include "../rpc.hpp"
40
41#include <nlohmann/json.hpp>
42
43namespace rpc_hpp
44{
45namespace adapters
46{
47 class njson_adapter;
48
49 template<>
51 {
52 using serial_t = nlohmann::json;
53 using bytes_t = std::string;
54 };
55
56 class njson_adapter : public detail::serial_adapter_base<njson_adapter>
57 {
58 public:
59 [[nodiscard]] static std::string to_bytes(nlohmann::json&& serial_obj)
60 {
61 return std::move(serial_obj).dump();
62 }
63
64 [[nodiscard]] static std::optional<nlohmann::json> from_bytes(std::string&& bytes)
65 {
66 nlohmann::json obj;
67
68 try
69 {
70 obj = nlohmann::json::parse(std::move(bytes));
71 }
72 catch (const nlohmann::json::parse_error&)
73 {
74 return std::nullopt;
75 }
76
77 if (!obj.is_object())
78 {
79 return std::nullopt;
80 }
81
82 if (obj.contains("except_type"))
83 {
84 if (obj["except_type"] != 0 && !obj.contains("err_mesg"))
85 {
86 return std::nullopt;
87 }
88
89 // Objects with exceptions can be otherwise empty
90 return std::make_optional(std::move(obj));
91 }
92
93 if (const auto fname_it = obj.find("func_name");
94 fname_it == obj.end() || !fname_it->is_string() || fname_it->empty())
95 {
96 return std::nullopt;
97 }
98
99 if (const auto args_it = obj.find("args"); args_it == obj.end() || !args_it->is_array())
100 {
101 return std::nullopt;
102 }
103
104 return std::make_optional(std::move(obj));
105 }
106
107 static nlohmann::json empty_object() { return nlohmann::json::object(); }
108
109 template<typename R, typename... Args>
110 [[nodiscard]] static nlohmann::json serialize_pack(
111 const detail::packed_func<R, Args...>& pack)
112 {
113 nlohmann::json obj{};
114 obj["func_name"] = pack.get_func_name();
115 obj["args"] = nlohmann::json::array();
116 auto& arg_arr = obj["args"];
117 arg_arr.get_ref<nlohmann::json::array_t&>().reserve(sizeof...(Args));
118
119 detail::for_each_tuple(pack.get_args(),
120 [&arg_arr](auto&& elem)
121 { push_args(std::forward<decltype(elem)>(elem), arg_arr); });
122
123 if (!pack)
124 {
125 obj["except_type"] = pack.get_except_type();
126 obj["err_mesg"] = pack.get_err_mesg();
127 return obj;
128 }
129
130 if constexpr (!std::is_void_v<R>)
131 {
132 obj["result"] = {};
133 push_arg(pack.get_result(), obj["result"]);
134 }
135
136 return obj;
137 }
138
139 template<typename R, typename... Args>
140 [[nodiscard]] static detail::packed_func<R, Args...> deserialize_pack(
141 const nlohmann::json& serial_obj)
142 {
143 const auto& args_val = serial_obj["args"];
144 [[maybe_unused]] unsigned arg_counter = 0;
145 typename detail::packed_func<R, Args...>::args_t args{ parse_args<Args>(
146 args_val, arg_counter)... };
147
148 if constexpr (std::is_void_v<R>)
149 {
150 detail::packed_func<void, Args...> pack(serial_obj["func_name"], std::move(args));
151
152 if (serial_obj.contains("except_type"))
153 {
154 pack.set_exception(serial_obj["err_mesg"],
155 static_cast<exception_type>(serial_obj["except_type"]));
156 }
157
158 return pack;
159 }
160 else
161 {
162 if (serial_obj.contains("result") && !serial_obj["result"].is_null())
163 {
164 return detail::packed_func<R, Args...>(serial_obj["func_name"],
165 parse_arg<R>(serial_obj["result"]), std::move(args));
166 }
167
168 detail::packed_func<R, Args...> pack(
169 serial_obj["func_name"], std::nullopt, std::move(args));
170
171 if (serial_obj.contains("except_type"))
172 {
173 pack.set_exception(serial_obj["err_mesg"],
174 static_cast<exception_type>(serial_obj["except_type"]));
175 }
176
177 return pack;
178 }
179 }
180
181 [[nodiscard]] static std::string get_func_name(const nlohmann::json& serial_obj)
182 {
183 return serial_obj["func_name"];
184 }
185
186 [[nodiscard]] static rpc_exception extract_exception(const nlohmann::json& serial_obj)
187 {
188 return rpc_exception{ serial_obj.at("err_mesg").get<std::string>(),
189 static_cast<exception_type>(serial_obj.at("except_type").get<int>()) };
190 }
191
192 static void set_exception(nlohmann::json& serial_obj, const rpc_exception& ex)
193 {
194 serial_obj["except_type"] = ex.get_type();
195 serial_obj["err_mesg"] = ex.what();
196 }
197
198 template<typename T>
199 static nlohmann::json serialize(const T& val) = delete;
200
201 template<typename T>
202 static T deserialize(const nlohmann::json& serial_obj) = delete;
203
204 private:
205 // nodiscard because this function is pointless without checking the bool
206 template<typename T>
207 [[nodiscard]] static constexpr bool validate_arg(const nlohmann::json& arg) noexcept
208 {
209 if constexpr (std::is_same_v<T, bool>)
210 {
211 return arg.is_boolean();
212 }
213 else if constexpr (std::is_integral_v<T>)
214 {
215 return arg.is_number() && !arg.is_number_float();
216 }
217 else if constexpr (std::is_floating_point_v<T>)
218 {
219 return arg.is_number_float();
220 }
221 else if constexpr (std::is_same_v<T, std::string>)
222 {
223 return arg.is_string();
224 }
225 else if constexpr (detail::is_container_v<T>)
226 {
227 return arg.is_array();
228 }
229 else
230 {
231 return !arg.is_null();
232 }
233 }
234
235 // nodiscard because expect_type is consumed by the function
236 [[nodiscard]] static std::string mismatch_string(
237 std::string&& expect_type, const nlohmann::json& arg)
238 {
239 return { "njson expected type: " + std::move(expect_type)
240 + ", got type: " + arg.type_name() };
241 }
242
243 template<typename T>
244 static void push_arg(T&& arg, nlohmann::json& obj)
245 {
246 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
247
248 if constexpr (std::is_arithmetic_v<no_ref_t> || std::is_same_v<no_ref_t, std::string>)
249 {
250 obj = std::forward<T>(arg);
251 }
252 else if constexpr (detail::is_container_v<no_ref_t>)
253 {
254 obj = nlohmann::json::array();
255 obj.get_ref<nlohmann::json::array_t&>().reserve(arg.size());
256
257 for (auto&& val : arg)
258 {
259 push_args(std::forward<decltype(val)>(val), obj);
260 }
261 }
262 else if constexpr (detail::is_serializable_v<njson_adapter, no_ref_t>)
263 {
264 obj = no_ref_t::template serialize<njson_adapter>(std::forward<T>(arg));
265 }
266 else
267 {
268 obj = serialize<no_ref_t>(std::forward<T>(arg));
269 }
270 }
271
272 template<typename T>
273 static void push_args(T&& arg, nlohmann::json& obj_arr)
274 {
275 nlohmann::json tmp{};
276 push_arg(std::forward<T>(arg), tmp);
277 obj_arr.push_back(std::move(tmp));
278 }
279
280 // nodiscard because parsing can be expensive, and it makes no sense to not use the parsed result
281 template<typename T>
282 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_arg(
283 const nlohmann::json& arg)
284 {
285 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
286
287 if (!validate_arg<T>(arg))
288 {
289 throw function_mismatch(mismatch_string(typeid(T).name(), arg));
290 }
291
292 if constexpr (std::is_arithmetic_v<no_ref_t> || std::is_same_v<no_ref_t, std::string>)
293 {
294 return arg.get<no_ref_t>();
295 }
296 else if constexpr (detail::is_container_v<no_ref_t>)
297 {
298 using value_t = typename no_ref_t::value_type;
299
300 no_ref_t container{};
301 container.reserve(arg.size());
302 unsigned arg_counter = 0;
303
304 for (const auto& val : arg)
305 {
306 container.push_back(parse_args<value_t>(val, arg_counter));
307 }
308
309 return container;
310 }
311 else if constexpr (detail::is_serializable_v<njson_adapter, no_ref_t>)
312 {
313 return no_ref_t::template deserialize<njson_adapter>(arg);
314 }
315 else
316 {
317 return deserialize<no_ref_t>(arg);
318 }
319 }
320
321 // nodiscard because parsing can be expensive, and it makes no sense to not use the parsed result
322 template<typename T>
323 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_args(
324 const nlohmann::json& arg_arr, unsigned& index)
325 {
326 if (index >= arg_arr.size())
327 {
328 throw function_mismatch("Argument count mismatch");
329 }
330
331 const auto& arg = arg_arr.is_array() ? arg_arr[index++] : arg_arr;
332 return parse_arg<T>(arg);
333 }
334 };
335} // namespace adapters
336} // namespace rpc_hpp
Definition: rpc_njson.hpp:57
Definition: rpc.hpp:182
Definition: rpc.hpp:107
Top-level namespace for rpc.hpp classes and functions.
Definition: rpc.hpp:88
Definition: rpc.hpp:254