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
13#include "daw_json_assert.h"
15#include "daw_json_name.h"
18#include "daw_json_skip.h"
20
21#include <daw/daw_consteval.h>
22#include <daw/daw_constinit.h>
23#include <daw/daw_likely.h>
24#include <daw/daw_traits.h>
25
26#include <cstddef>
27#include <type_traits>
28
29namespace daw::json {
30 inline namespace DAW_JSON_VER {
31 namespace json_details {
32
45 template<typename JsonMember, typename ParseState>
46 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr json_result_t<JsonMember>
47 parse_ordered_class_member( std::size_t &member_index,
48 ParseState &parse_state ) {
49
50 using json_member_t = ordered_member_subtype_t<JsonMember>;
51
52 parse_state.move_next_member_or_end( );
57 if constexpr( is_an_ordered_member_v<JsonMember> ) {
58 pocm_details::maybe_skip_members<is_json_nullable_v<json_member_t>>(
59 parse_state, member_index, JsonMember::member_index );
60 } else {
61 daw_json_assert_weak( parse_state.has_more( ),
62 ErrorReason::UnexpectedEndOfData, parse_state );
63 }
64
65 // this is an out value, get position ready
66 ++member_index;
67
68 if( DAW_UNLIKELY( parse_state.front( ) == ']' ) ) {
69 if constexpr( is_json_nullable_v<json_member_t> ) {
70
71 auto loc = ParseState{ };
72 return parse_value<json_member_t, true,
73 json_member_t::expected_type>( loc );
74 } else {
75 daw_json_error( missing_member( "ordered_class_member" ),
76 parse_state );
77 }
78 }
79 return parse_value<json_member_t, false, json_member_t::expected_type>(
80 parse_state );
81 }
82
93 template<std::size_t member_position, typename JsonMember,
94 AllMembersMustExist must_exist, bool NeedsClassPositions,
95 typename ParseState, std::size_t N, typename CharT, bool B>
96 [[nodiscard]] DAW_ATTRIB_FLATINLINE static constexpr json_result_t<
97 JsonMember>
98 parse_class_member( ParseState &parse_state,
99 locations_info_t<N, CharT, B> &locations ) {
100 parse_state.move_next_member_or_end( );
101
103 not parse_state.empty( ) and parse_state.is_at_next_class_member( ),
104 ErrorReason::MissingMemberNameOrEndOfClass, parse_state );
105
106 auto [loc, known] = find_class_member<member_position, must_exist>(
107 parse_state, locations, is_json_nullable_v<JsonMember>,
108 JsonMember::name );
109
110 // If the member was found loc will have it's position
111 if( not known ) {
112 if constexpr( NeedsClassPositions ) {
113 auto const cf = parse_state.class_first;
114 auto const cl = parse_state.class_last;
115 if constexpr( is_pinned_type_v<
116 json_result_t<without_name<JsonMember>>> ) {
117 auto const after_parse = daw::on_scope_exit( [&] {
118 parse_state.class_first = cf;
119 parse_state.class_last = cl;
120 } );
121 return parse_value<without_name<JsonMember>, false,
122 JsonMember::expected_type>( parse_state );
123 } else {
124 auto result =
125 parse_value<without_name<JsonMember>, false,
126 JsonMember::expected_type>( parse_state );
127 parse_state.class_first = cf;
128 parse_state.class_last = cl;
129 return result;
130 }
131 } else {
132 return parse_value<without_name<JsonMember>, false,
133 JsonMember::expected_type>( parse_state );
134 }
135 }
136 // We cannot find the member, check if the member is nullable
137 if( loc.is_null( ) ) {
138 if constexpr( is_json_nullable_v<JsonMember> ) {
139 return parse_value_null<without_name<JsonMember>, true>( loc );
140 } else {
141 daw_json_error( missing_member( std::string_view(
142 std::data( JsonMember::name ),
143 std::size( JsonMember::name ) ) ),
144 parse_state );
145 }
146 }
147
148 // Member was previously skipped
149 return parse_value<without_name<JsonMember>, true,
150 JsonMember::expected_type>( loc );
151 }
152
153 template<bool IsExactClass, typename ParseState, typename OldClassPos>
154 DAW_ATTRIB_INLINE static constexpr void
155 class_cleanup_now( ParseState &parse_state,
156 OldClassPos const &old_class_pos ) {
157 daw_json_assert_weak( parse_state.has_more( ),
158 ErrorReason::UnexpectedEndOfData, parse_state );
159 parse_state.move_next_member_or_end( );
160 // If we fulfill the contract before all values are parses
161 parse_state.move_to_next_class_member( );
162 if constexpr( IsExactClass ) {
163 daw_json_assert_weak( parse_state.front( ) == '}',
164 ErrorReason::UnknownMember, parse_state );
165 parse_state.remove_prefix( );
166 } else {
167 (void)parse_state.skip_class( );
168 // Yes this must be checked. We maybe at the end of document. After
169 // the 2nd try, give up
170 }
171 parse_state.trim_left_checked( );
172 parse_state.set_class_position( old_class_pos );
173 }
174
182 template<typename JsonClass, typename... JsonMembers, typename ParseState,
183 std::size_t... Is>
184 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result_t<JsonClass>
185 parse_json_class( ParseState &parse_state, std::index_sequence<Is...> ) {
186 static_assert( is_a_json_type_v<JsonClass> );
187 using T = json_result_t<JsonClass>;
188 using Constructor = json_constructor_t<JsonClass>;
189 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
190 using must_exist =
191 daw::constant<( all_json_members_must_exist_v<T, ParseState>
192 ? AllMembersMustExist::yes
193 : AllMembersMustExist::no )>;
194
195 parse_state.trim_left( );
196 // TODO, use member name
197 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
198 ErrorReason::InvalidClassStart, parse_state );
199
200 auto const old_class_pos = parse_state.get_class_position( );
201 parse_state.set_class_position( );
202 parse_state.remove_prefix( );
203 parse_state.trim_left( );
204
205 if constexpr( sizeof...( JsonMembers ) == 0 ) {
206 // Clang-CL with MSVC has issues if we don't do empties this way
207 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
208 parse_state, old_class_pos );
209
210 if constexpr( should_construct_explicitly_v<Constructor, T,
211 ParseState> ) {
212 return T{ };
213 } else {
214 return construct_value_tp<T, Constructor>( parse_state,
215 fwd_pack{ } );
216 }
217 } else {
218 using NeedClassPositions = std::bool_constant<(
219 ( must_be_class_member_v<typename JsonMembers::without_name> or
220 ... ) )>;
221
222#if defined( DAW_JSON_BUGFIX_MSVC_KNOWN_LOC_ICE_003 )
223 auto known_locations =
224 make_locations_info<ParseState, JsonMembers...>( );
225#else
226 auto known_locations = DAW_AS_CONSTANT(
227 ( make_locations_info<ParseState, JsonMembers...>( ) ) );
228#endif
229
230 if constexpr( is_pinned_type_v<json_result_t<JsonClass>> ) {
234 auto const run_after_parse = daw::on_exit_success( [&] {
235 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
236 parse_state, old_class_pos );
237 } );
238 (void)run_after_parse;
239
240 if constexpr( should_construct_explicitly_v<Constructor, T,
241 ParseState> ) {
242 return T{ parse_class_member<
243 Is, daw::traits::nth_type<Is, JsonMembers...>,
244 must_exist::value, NeedClassPositions::value>(
245 parse_state, known_locations )... };
246 } else {
247 return construct_value_tp<T, Constructor>(
248 parse_state, fwd_pack{ parse_class_member<
249 Is, daw::traits::nth_type<Is, JsonMembers...>,
250 must_exist::value, NeedClassPositions::value>(
251 parse_state, known_locations )... } );
252 }
253 } else {
254 if constexpr( should_construct_explicitly_v<Constructor, T,
255 ParseState> ) {
256 auto result = T{ parse_class_member<
257 Is, daw::traits::nth_type<Is, JsonMembers...>,
258 must_exist::value, NeedClassPositions::value>(
259 parse_state, known_locations )... };
260
261 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
262 parse_state, old_class_pos );
263 return result;
264 } else {
265 auto result = construct_value_tp<T, Constructor>(
266 parse_state, fwd_pack{ parse_class_member<
267 Is, daw::traits::nth_type<Is, JsonMembers...>,
268 must_exist::value, NeedClassPositions::value>(
269 parse_state, known_locations )... } );
270
271 class_cleanup_now<all_json_members_must_exist_v<T, ParseState>>(
272 parse_state, old_class_pos );
273 return result;
274 }
275 }
276 }
277 }
278
284 template<typename JsonClass, typename... JsonMembers, typename ParseState>
285 [[nodiscard]] static inline constexpr json_result_t<JsonClass>
286 parse_json_tuple_class( ParseState &parse_state ) {
287 static_assert( is_a_json_type_v<JsonClass> );
288 using T = json_base_type_t<JsonClass>;
289 using Constructor = json_constructor_t<JsonClass>;
290 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
291 static_assert(
292 std::is_invocable_v<Constructor, json_result_t<JsonMembers>...>,
293 "Supplied types cannot be used for construction of this type" );
294
295 parse_state.trim_left( ); // Move to array start '['
296 daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
297 ErrorReason::InvalidArrayStart, parse_state );
298 auto const old_class_pos = parse_state.get_class_position( );
299 parse_state.set_class_position( );
300 parse_state.remove_prefix( );
301 parse_state.trim_left( );
302
303 std::size_t current_idx = 0;
304
305 if constexpr( is_pinned_type_v<json_result_t<JsonClass>> ) {
306 auto const run_after_parse = daw::on_exit_success( [&] {
307 ordered_class_cleanup<all_json_members_must_exist_v<T, ParseState>,
308 ParseState, decltype( old_class_pos )>(
309 parse_state, old_class_pos );
310 } );
311 (void)run_after_parse;
312 if constexpr( should_construct_explicitly_v<Constructor, T,
313 ParseState> ) {
314 return T{ parse_ordered_class_member<JsonMembers>(
315 current_idx, parse_state )... };
316 } else {
317 return construct_value_tp<T, Constructor>(
318 parse_state, fwd_pack{ parse_ordered_class_member<JsonMembers>(
319 current_idx, parse_state )... } );
320 }
321 } else {
322 auto result = [&] {
323 if constexpr( should_construct_explicitly_v<Constructor, T,
324 ParseState> ) {
325 return T{ parse_ordered_class_member<JsonMembers>(
326 current_idx, parse_state )... };
327 } else {
328 return construct_value_tp<T, Constructor>(
329 parse_state, fwd_pack{ parse_ordered_class_member<JsonMembers>(
330 current_idx, parse_state )... } );
331 }
332 }( );
333 if constexpr( all_json_members_must_exist_v<T, ParseState> ) {
334 parse_state.trim_left( );
335 daw_json_assert_weak( parse_state.front( ) == ']',
336 ErrorReason::UnknownMember, parse_state );
337 parse_state.remove_prefix( );
338 parse_state.trim_left( );
339 } else {
340 (void)parse_state.skip_array( );
341 }
342 parse_state.set_class_position( old_class_pos );
343 return result;
344 }
345 }
346 } // namespace json_details
347 } // namespace DAW_JSON_VER
348} // 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.
DAW_ATTRIB_NOINLINE void daw_json_error(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