DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_class.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
11#include "version.h"
12
20
21#include <daw/daw_callable.h>
22#include <daw/daw_consteval.h>
23#include <daw/daw_constinit.h>
24#include <daw/daw_likely.h>
25#include <daw/daw_traits.h>
26#include <daw/traits/daw_traits_nth_element.h>
27
28#include <cstddef>
29#include <type_traits>
30
31namespace daw::json {
32 inline namespace DAW_JSON_VER {
33 namespace json_details {
34
47 template<typename JsonMember, typename ParseState>
48 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr json_result_t<JsonMember>
49 parse_ordered_class_member( std::size_t &member_index,
50 ParseState &parse_state ) {
51
52 using json_member_t = ordered_member_subtype_t<JsonMember>;
53
54 parse_state.move_next_member_or_end( );
59 if constexpr( is_an_ordered_member_v<JsonMember> ) {
60 pocm_details::maybe_skip_members<is_json_nullable_v<json_member_t>>(
61 parse_state, member_index, JsonMember::member_index );
62 } else {
63 daw_json_assert_weak( parse_state.has_more( ),
64 ErrorReason::UnexpectedEndOfData,
65 parse_state );
66 }
67
68 // this is an out value, get position ready
69 ++member_index;
70
71 if( DAW_UNLIKELY( parse_state.front( ) == ']' ) ) {
72 if constexpr( is_json_nullable_v<json_member_t> ) {
73
74 auto loc = ParseState{ };
75 return parse_value<json_member_t,
76 true,
77 json_member_t::expected_type>( loc );
78 } else {
80 true, missing_member( "ordered_class_member" ), parse_state );
81 }
82 }
83 return parse_value<json_member_t, false, json_member_t::expected_type>(
84 parse_state );
85 }
86
97 template<typename JsonMember, bool NeedsClassPositions,
98 typename ParseState>
99 [[nodiscard]] constexpr json_result_t<JsonMember>
100 parse_class_member_impl( ParseState &parse_state,
101 find_result<ParseState> fr ) {
102
103 auto [loc, known] = fr;
104
105 // If the member was found loc will have it's position
106 if( not known ) {
107 if constexpr( NeedsClassPositions ) {
108 auto const cf = parse_state.class_first;
109 auto const cl = parse_state.class_last;
110 if constexpr( is_pinned_type_v<
111 json_result_t<without_name<JsonMember>>> ) {
112 auto const after_parse = daw::on_scope_exit( [&] {
113 parse_state.class_first = cf;
114 parse_state.class_last = cl;
115 } );
116 return parse_value<without_name<JsonMember>,
117 false,
118 JsonMember::expected_type>( parse_state );
119 } else {
120 auto result =
121 parse_value<without_name<JsonMember>,
122 false,
123 JsonMember::expected_type>( parse_state );
124 parse_state.class_first = cf;
125 parse_state.class_last = cl;
126 return result;
127 }
128 } else {
129 return parse_value<without_name<JsonMember>,
130 false,
131 JsonMember::expected_type>( parse_state );
132 }
133 }
134 // We cannot find the member, check if the member is nullable
135 if( loc.is_null( ) ) {
136 if constexpr( is_json_nullable_v<JsonMember> ) {
137 return parse_value_null<without_name<JsonMember>, true>( loc );
138 } else {
139 daw_json_ensure( not loc.is_null( ),
140 missing_member( std::string_view(
141 std::data( JsonMember::name ),
142 std::size( JsonMember::name ) ) ),
143 parse_state );
144 }
145 }
146
147 // Member was previously skipped
148 return parse_value<without_name<JsonMember>,
149 true,
150 JsonMember::expected_type>( loc );
151 }
152
153 template<std::size_t member_position, typename JsonMember,
154 AllMembersMustExist must_exist, bool NeedsClassPositions,
155 typename ParseState, std::size_t N, bool B>
156 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result_t<JsonMember>
157 parse_class_member( ParseState &parse_state,
158 locations_info_t<N, B> &locations ) {
159 parse_state.move_next_member_or_end( );
160
161 daw_json_assert_weak( not parse_state.empty( ) and
162 parse_state.is_at_next_class_member( ),
163 ErrorReason::MissingMemberNameOrEndOfClass,
164 parse_state );
165
166 return parse_class_member_impl<JsonMember, NeedsClassPositions>(
167 parse_state,
168 find_class_member<member_position, must_exist>(
169 parse_state,
170 locations,
171 is_json_nullable_v<JsonMember>,
172 JsonMember::name ) );
173 }
174
175 template<bool IsExactClass, typename ParseState, typename OldClassPos>
176 DAW_ATTRIB_INLINE static constexpr void
177 class_cleanup_now( ParseState &parse_state,
178 OldClassPos const &old_class_pos ) {
179 daw_json_assert_weak( parse_state.has_more( ),
180 ErrorReason::UnexpectedEndOfData,
181 parse_state );
182 parse_state.move_next_member_or_end( );
183 // If we fulfill the contract before all values are parses
184 parse_state.move_to_next_class_member( );
185 if constexpr( IsExactClass ) {
186 daw_json_assert_weak( parse_state.front( ) == '}',
187 ErrorReason::UnknownMember,
188 parse_state );
189 parse_state.remove_prefix( );
190 } else {
191 (void)parse_state.skip_class( );
192 // Yes this must be checked. We maybe at the end of document. After
193 // the 2nd try, give up
194 }
195 parse_state.trim_left_checked( );
196 parse_state.set_class_position( old_class_pos );
197 }
198
206 template<typename JsonClass, typename... JsonMembers, typename ParseState,
207 std::size_t... Is>
208 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result_t<JsonClass>
209 parse_json_class( ParseState &parse_state, std::index_sequence<Is...> ) {
210 static_assert( is_a_json_type_v<JsonClass> );
211 using T = json_result_t<JsonClass>;
212 using Constructor = json_constructor_t<JsonClass>;
213 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
214 using must_exist =
215 daw::constant<( all_json_members_must_exist_v<T, ParseState>
216 ? AllMembersMustExist::yes
217 : AllMembersMustExist::no )>;
218
219 parse_state.trim_left( );
220 // TODO, use member name
221 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
222 ErrorReason::InvalidClassStart,
223 parse_state );
224
225 auto const old_class_pos = parse_state.get_class_position( );
226 parse_state.set_class_position( );
227 parse_state.remove_prefix( );
228 parse_state.trim_left( );
229
230 if constexpr( sizeof...( JsonMembers ) == 0 ) {
231 // Clang-CL with MSVC has issues if we don't do empties this way
232 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
233 parse_state, old_class_pos );
234
235 if constexpr( should_construct_explicitly_v<Constructor,
236 T,
237 ParseState> ) {
238 return T{ };
239 } else {
240 return construct_value_tp<T, Constructor>( parse_state,
241 fwd_pack{ } );
242 }
243 } else {
244 using NeedClassPositions = std::bool_constant<(
245 ( must_be_class_member_v<typename JsonMembers::without_name> or
246 ... ) )>;
247
248#if defined( DAW_JSON_BUGFIX_MSVC_KNOWN_LOC_ICE_003 )
249 auto known_locations =
250 make_locations_info<ParseState, JsonMembers...>( );
251#else
252 auto known_locations = DAW_AS_CONSTANT(
253 ( make_locations_info<ParseState, JsonMembers...>( ) ) );
254#endif
255
256 if constexpr( is_pinned_type_v<json_result_t<JsonClass>> ) {
260 auto const run_after_parse = daw::on_exit_success( [&] {
261 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
262 parse_state, old_class_pos );
263 } );
264 (void)run_after_parse;
265
266 if constexpr( should_construct_explicitly_v<Constructor,
267 T,
268 ParseState> ) {
269 return T{
270 parse_class_member<Is,
271 daw::traits::nth_element<Is, JsonMembers...>,
272 must_exist::value,
273 NeedClassPositions::value>(
274 parse_state, known_locations )... };
275 } else {
276 return construct_value_tp<T, Constructor>(
277 parse_state,
278 fwd_pack{ parse_class_member<
279 Is,
280 daw::traits::nth_element<Is, JsonMembers...>,
281 must_exist::value,
282 NeedClassPositions::value>( parse_state,
283 known_locations )... } );
284 }
285 } else {
286 if constexpr( should_construct_explicitly_v<Constructor,
287 T,
288 ParseState> ) {
289 auto result = T{
290 parse_class_member<Is,
291 daw::traits::nth_element<Is, JsonMembers...>,
292 must_exist::value,
293 NeedClassPositions::value>(
294 parse_state, known_locations )... };
295
296 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
297 parse_state, old_class_pos );
298 return result;
299 } else {
300 auto result = construct_value_tp<T, Constructor>(
301 parse_state,
302 fwd_pack{ parse_class_member<
303 Is,
304 daw::traits::nth_element<Is, JsonMembers...>,
305 must_exist::value,
306 NeedClassPositions::value>( parse_state,
307 known_locations )... } );
308
309 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
310 parse_state, old_class_pos );
311 return result;
312 }
313 }
314 }
315 }
316#if defined( DAW_JSON_HAS_REFLECTION )
317 template<typename JsonClass, typename... JsonMembers, typename ParseState,
318 std::size_t... Is>
319 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result_t<JsonClass>
320 parse_json_reflected_class( ParseState &parse_state,
321 std::index_sequence<Is...> ) {
322 static_assert( sizeof...( JsonMembers ) > 0 );
323 using T = json_result_t<JsonClass>;
324 static_assert( refl_details::PotentiallyReflectable<T> );
325
326 static constexpr auto must_exist =
327 all_json_members_must_exist_v<T, ParseState>
328 ? AllMembersMustExist::yes
329 : AllMembersMustExist::no;
330
331 parse_state.trim_left( );
332
333 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
334 ErrorReason::InvalidClassStart,
335 parse_state );
336
337 auto const old_class_pos = parse_state.get_class_position( );
338 parse_state.set_class_position( );
339 parse_state.remove_prefix( );
340 parse_state.trim_left( );
341
342 static constexpr auto NeedClassPositions =
343 ( must_be_class_member_v<typename JsonMembers::without_name> or ... );
344
345 auto known_locations =
346 make_locations_info<ParseState, JsonMembers...>( );
347
348 auto result = T{ parse_class_member<Is,
349 JsonMembers...[Is],
350 must_exist,
351 NeedClassPositions>(
352 parse_state, known_locations )... };
353
354 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
355 parse_state, old_class_pos );
356 return result;
357 }
358#endif
364 template<typename JsonClass, typename... JsonMembers, typename ParseState>
365 [[nodiscard]] static constexpr json_result_t<JsonClass>
366 parse_json_tuple_class( ParseState &parse_state ) {
367 static_assert( is_a_json_type_v<JsonClass> );
368 using T = json_base_type_t<JsonClass>;
369 using Constructor = json_constructor_t<JsonClass>;
370 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
371 static_assert(
372 daw::is_callable_v<Constructor, json_result_t<JsonMembers>...>,
373 "Supplied types cannot be used for construction of this type" );
374
375 parse_state.trim_left( ); // Move to array start '['
376 daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
377 ErrorReason::InvalidArrayStart,
378 parse_state );
379 auto const old_class_pos = parse_state.get_class_position( );
380 parse_state.set_class_position( );
381 parse_state.remove_prefix( );
382 parse_state.trim_left( );
383
384 std::size_t current_idx = 0;
385
386 if constexpr( is_pinned_type_v<json_result_t<JsonClass>> ) {
387 auto const run_after_parse = daw::on_exit_success( [&] {
388 ordered_class_cleanup<all_json_members_must_exist_v<T, ParseState>,
389 ParseState,
390 decltype( old_class_pos )>( parse_state,
391 old_class_pos );
392 } );
393 (void)run_after_parse;
394 if constexpr( should_construct_explicitly_v<Constructor,
395 T,
396 ParseState> ) {
397 return T{ parse_ordered_class_member<JsonMembers>(
398 current_idx, parse_state )... };
399 } else {
400 return construct_value_tp<T, Constructor>(
401 parse_state,
402 fwd_pack{ parse_ordered_class_member<JsonMembers>(
403 current_idx, parse_state )... } );
404 }
405 } else {
406 auto result = [&] {
407 if constexpr( should_construct_explicitly_v<Constructor,
408 T,
409 ParseState> ) {
410 return T{ parse_ordered_class_member<JsonMembers>(
411 current_idx, parse_state )... };
412 } else {
413 return construct_value_tp<T, Constructor>(
414 parse_state,
415 fwd_pack{ parse_ordered_class_member<JsonMembers>(
416 current_idx, parse_state )... } );
417 }
418 }( );
419 if constexpr( all_json_members_must_exist_v<T, ParseState> ) {
420 parse_state.trim_left( );
421 daw_json_assert_weak( parse_state.front( ) == ']',
422 ErrorReason::UnknownMember,
423 parse_state );
424 parse_state.remove_prefix( );
425 parse_state.trim_left( );
426 } else {
427 (void)parse_state.skip_array( );
428 }
429 parse_state.set_class_position( old_class_pos );
430 return result;
431 }
432 }
433 } // namespace json_details
434 } // namespace DAW_JSON_VER
435} // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Assert that Bool is true when in Checked Input mode If false pass rest of args to daw_json_error.
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
DAW_ATTRIB_NOINLINE void daw_json_error(bool b, ErrorReason reason)
constexpr bool is_pinned_type_v
Is the type pinned in memory and unable to be copied/moved after construction(e.g....
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20