DAW JSON Link
daw_json_parse_name.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 "daw_json_assert.h"
14 
15 #include <ciso646>
16 
17 namespace daw::json::json_details::name {
18  namespace name_parser {
19  /*
20  * end of string " -> name value separating : -> any white space
21  * the string can be escaped too
22  */
23  template<typename Range>
24  [[maybe_unused]] static constexpr void trim_end_of_name( Range &rng ) {
25  rng.trim_left( );
26  // TODO: should we check for end
27  daw_json_assert_weak( rng.front( ) == ':', ErrorReason::InvalidMemberName,
28  rng );
29  rng.remove_prefix( );
30  rng.trim_left( );
31  }
32 
33  template<typename Range>
34  [[nodiscard,
35  maybe_unused]] DAW_ATTRIBUTE_FLATTEN static inline constexpr daw::
36  string_view
37  parse_nq( Range &rng ) {
38 
39  if constexpr( Range::allow_escaped_names ) {
40  auto r = skip_string_nq( rng );
41  trim_end_of_name( rng );
42  return daw::string_view( r.data( ), r.size( ) );
43  } else {
44  char const *const ptr = rng.first;
45  if constexpr( Range::is_unchecked_input ) {
46  rng.template move_to_next_of_unchecked<'"'>( );
47  } else {
48  rng.template move_to_next_of_checked<'"'>( );
49  }
50  daw_json_assert_weak( rng.is_quotes_checked( ) and
51  *std::prev( rng.first ) != '\\',
52  ErrorReason::InvalidString, rng );
53  auto result = daw::string_view( ptr, rng.first );
54  rng.remove_prefix( );
55  trim_end_of_name( rng );
56  return result;
57  }
58  }
59  } // namespace name_parser
60 } // namespace daw::json::json_details::name
61 
62 namespace daw::json::json_details {
63 
64  // Paths are specified with dot separators, if the name has a dot in it,
65  // it must be escaped
66  // memberA.memberB.member\.C has 3 parts['memberA', 'memberB', 'member.C']
67  [[nodiscard]] constexpr auto pop_json_path( daw::string_view &path ) {
68  struct pop_json_path_result {
69  daw::string_view current{ };
70  char found_char = 0;
71  } result{ };
72  if( path.empty( ) ) {
73  return result;
74  }
75  if( path.front( ) == '.' ) {
76  path.remove_prefix( );
77  }
78  result.current = path.pop_front( [&, in_escape = false]( char c ) mutable {
79  if( in_escape ) {
80  in_escape = false;
81  return false;
82  }
83  switch( c ) {
84  case '\\':
85  in_escape = true;
86  return false;
87  case '.':
88  case '[':
89  case ']':
90  result.found_char = c;
91  return true;
92  default:
93  return false;
94  }
95  } );
96  return result;
97  }
98 
99  [[nodiscard]] constexpr bool
100  json_path_compare( daw::string_view json_path_item,
101  daw::string_view member_name ) {
102  if( json_path_item.front( ) == '\\' ) {
103  json_path_item.remove_prefix( );
104  }
105  while( not json_path_item.empty( ) and not member_name.empty( ) ) {
106  if( json_path_item.front( ) != member_name.front( ) ) {
107  return false;
108  }
109  json_path_item.remove_prefix( );
110  if( not json_path_item.empty( ) and json_path_item.front( ) == '\\' ) {
111  json_path_item.remove_prefix( );
112  }
113  member_name.remove_prefix( );
114  }
115  return json_path_item.size( ) == member_name.size( );
116  }
117 
118  // Get the next member name
119  // Assumes that the current item in stream is a double quote
120  // Ensures that the stream is left at the position of the associated
121  // value(e.g after the colon(:) and trimmed)
122  template<typename Range>
123  [[nodiscard]] constexpr daw::string_view parse_name( Range &rng ) {
124  daw_json_assert_weak( rng.is_quotes_checked( ),
125  ErrorReason::InvalidMemberName, rng );
126  rng.remove_prefix( );
127  return name::name_parser::parse_nq( rng );
128  }
129 
130  template<typename Range>
131  constexpr bool find_range2( Range &rng, daw::string_view path ) {
132 
133  auto pop_result = pop_json_path( path );
134  while( not pop_result.current.empty( ) ) {
135  if( pop_result.found_char == ']' ) {
136  // Array Index
137  daw_json_assert_weak( rng.is_opening_bracket_checked( ),
138  ErrorReason::InvalidJSONPath, rng );
139  rng.remove_prefix( );
140  rng.trim_left_unchecked( );
141  auto idx =
142  daw::parser::parse_unsigned_int<std::size_t>( pop_result.current );
143 
144  while( idx > 0 ) {
145  --idx;
146  (void)skip_value( rng );
147  rng.trim_left_checked( );
148  if( ( idx > 0 ) & ( rng.has_more( ) and ( rng.front( ) != ',' ) ) ) {
149  return false;
150  }
151  rng.clean_tail( );
152  }
153  } else {
154  daw_json_assert_weak( rng.is_opening_brace_checked( ),
155  ErrorReason::InvalidJSONPath, rng );
156  rng.remove_prefix( );
157  rng.trim_left_unchecked( );
158  auto name = parse_name( rng );
159  while( not json_path_compare( pop_result.current, name ) ) {
160  (void)skip_value( rng );
161  rng.clean_tail( );
162  if( rng.empty( ) or rng.front( ) != '"' ) {
163  return false;
164  }
165  name = parse_name( rng );
166  }
167  }
168  pop_result = pop_json_path( path );
169  }
170  return true;
171  }
172 
173  template<typename ParsePolicy, typename String>
174  [[nodiscard]] constexpr std::pair<bool, ParsePolicy>
175  find_range( String &&str, daw::string_view start_path ) {
176  static_assert( std::is_same_v<char const *, typename ParsePolicy::iterator>,
177  "Only char const * ranges are currently supported" );
178  auto rng =
179  ParsePolicy( std::data( str ), std::data( str ) + std::size( str ) );
180  rng.trim_left_checked( );
181  if( rng.has_more( ) and not start_path.empty( ) ) {
182  if( not find_range2( rng, start_path ) ) {
183  return { false, rng };
184  }
185  }
186  return { true, rng };
187  }
188 
189  template<typename ParsePolicy, typename String, typename Allocator>
190  [[nodiscard]] constexpr auto
191  find_range( String &&str, daw::string_view start_path, Allocator &alloc ) {
192  static_assert( std::is_same_v<char const *, typename ParsePolicy::iterator>,
193  "Only char const * ranges are currently supported" );
194  auto rng = ParsePolicy::with_allocator(
195  std::data( str ), std::data( str ) + std::size( str ), alloc );
196  rng.trim_left_checked( );
197  if( rng.has_more( ) and not start_path.empty( ) ) {
198  if( not find_range2( rng, start_path ) ) {
199  return std::pair{ false, rng };
200  }
201  }
202  return std::pair{ true, rng };
203  }
204 
205 } // namespace daw::json::json_details
daw_not_const_ex_functions.h
daw_json_assert_weak
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:206
daw_json_parse_std_string.h
daw_json_assert.h