DAW JSON Link
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"
14 #include "daw_json_location_info.h"
15 #include "daw_json_name.h"
16 #include "daw_json_parse_common.h"
17 #include "daw_json_parse_value.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 
29 namespace 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:25