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