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