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