DAW JSON Link
Loading...
Searching...
No Matches
daw_json_find_path.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 "impl/version.h"
12
14#include "daw_json_link_types.h"
16
17#include <daw/algorithms/daw_algorithm_accumulate.h>
18#include <daw/algorithms/daw_algorithm_find.h>
19#include <daw/daw_cpp_feature_check.h>
20#include <daw/daw_move.h>
21#include <daw/iterator/daw_reverse_iterator.h>
22
23#include <optional>
24#include <string>
25#include <string_view>
26#include <vector>
27
28namespace daw::json {
29 inline namespace DAW_JSON_VER {
30 namespace json_details {
31 struct less {
33 template<typename T, typename U>
34 DAW_ATTRIB_INLINE DAW_JSON_CPP23_STATIC_CALL_OP constexpr bool
35 operator( )( T const &lhs, U const &rhs )
37 return lhs < rhs;
38 }
40 };
41 } // namespace json_details
42
43 class json_path_node;
44
45 [[nodiscard]] inline std::vector<json_path_node>
46 find_json_path_stack_to( char const *parse_location,
47 char const *doc_start );
48
50 std::string_view m_name{ };
51 char const *m_value_start = nullptr;
52 long long m_index = -1;
53 JsonBaseParseTypes m_type = JsonBaseParseTypes::None;
54
55 friend std::vector<json_path_node>
56 find_json_path_stack_to( char const *parse_location,
57 char const *doc_start );
58
59 json_path_node( ) = default;
60 constexpr json_path_node( JsonBaseParseTypes Type, std::string_view Name,
61 long long Index, char const *ValueStart )
62 : m_name( Name )
63 , m_value_start( ValueStart )
64 , m_index( Index )
65 , m_type( Type ) {}
66
67 public:
69 [[nodiscard]] constexpr JsonBaseParseTypes type( ) const {
70 return m_type;
71 }
72
74 [[nodiscard]] constexpr std::string_view name( ) const {
75 return m_name;
76 }
77
79 [[nodiscard]] constexpr long long index( ) const {
80 return m_index;
81 }
82
84 [[nodiscard]] constexpr char const *value_start( ) const {
85 return m_value_start;
86 }
87 };
88
92 [[nodiscard]] DAW_ATTRIB_NOINLINE inline std::string
93 to_json_path_string( std::vector<json_path_node> const &path_stack ) {
94 return daw::algorithm::accumulate(
95 std::data( path_stack ), daw::data_end( path_stack ), std::string{ },
96 []( auto &&state, json_path_node const &sv )
98 if( sv.index( ) >= 0 ) {
99 state += '[';
100 state += std::to_string( sv.index( ) );
101 state += ']';
102 } else if( not sv.name( ).empty( ) ) {
103 state += '.';
104 state += std::string( sv.name( ) );
105 }
106 return DAW_FWD( state );
107 } );
108 }
109
114 [[nodiscard]] inline std::vector<json_path_node>
115 find_json_path_stack_to( char const *parse_location,
116 char const *doc_start ) {
117 if( parse_location == nullptr or doc_start == nullptr ) {
118 return { };
119 }
120 if( json_details::less{ }( parse_location, doc_start ) ) {
121 return { };
122 }
123
124 struct handler_t {
125 char const *first;
126 char const *last;
127 std::vector<json_path_node> parse_stack{ };
128
129 // This is for when we throw after array/class end, but before the
130 // next value starts
131 std::optional<json_path_node> last_popped{ };
132 json_path_node state{ };
133
134 JsonBaseParseTypes child_of( ) {
135 if( parse_stack.empty( ) ) {
136 return JsonBaseParseTypes::None;
137 }
138 return parse_stack.back( ).type( );
139 }
140
141 [[nodiscard]] bool handle_on_value( json_pair jp ) {
142 if( auto const range = jp.value.get_raw_state( );
143 range.empty( ) or last <= std::data( range ) ) {
144 return false;
145 }
146 if( auto const t = child_of( ); t == JsonBaseParseTypes::Class ) {
147 state.m_name = *jp.name;
148 state.m_index = -1;
149 } else if( t == JsonBaseParseTypes::Array ) {
150 state.m_name = { };
151 state.m_index++;
152 } else {
153 state.m_name = { };
154 state.m_index = -1;
155 }
156 state.m_value_start = jp.value.get_raw_state( ).first;
157 state.m_type = jp.value.type( );
158 last_popped = std::nullopt;
159 return true;
160 }
161
162 [[nodiscard]] bool handle_on_array_start( json_value const & ) {
163 parse_stack.push_back( state );
164 state = { };
165 return true;
166 }
167
168 [[nodiscard]] bool handle_on_array_end( ) {
169 if( not parse_stack.empty( ) ) {
170 last_popped = parse_stack.back( );
171 state = parse_stack.back( );
172 parse_stack.pop_back( );
173 }
174 return true;
175 }
176
177 [[nodiscard]] bool handle_on_class_start( json_value const & ) {
178 parse_stack.push_back( state );
179 state = { };
180 return true;
181 }
182
183 [[nodiscard]] bool handle_on_class_end( ) {
184 if( not parse_stack.empty( ) ) {
185 last_popped = parse_stack.back( );
186 state = parse_stack.back( );
187 parse_stack.pop_back( );
188 }
189 return true;
190 }
191
192 [[nodiscard]] bool handle_on_number( json_value jv ) {
193 auto sv = std::string_view( );
194#if defined( DAW_USE_EXCEPTIONS )
195 try {
196#endif
197 sv = jv.get_string_view( );
198#if defined( DAW_USE_EXCEPTIONS )
199 } catch( json_exception const & ) {
200 parse_stack.push_back( state );
201 return false;
202 }
203#endif
204 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
205 parse_stack.push_back( state );
206 return false;
207 }
208 return true;
209 }
210
211 [[nodiscard]] bool handle_on_bool( json_value jv ) {
212 auto sv = std::string_view( );
213#if defined( DAW_USE_EXCEPTIONS )
214 try {
215#endif
216 sv = jv.get_string_view( );
217#if defined( DAW_USE_EXCEPTIONS )
218 } catch( json_exception const & ) {
219 parse_stack.push_back( state );
220 return false;
221 }
222#endif
223 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
224 parse_stack.push_back( state );
225 return false;
226 }
227 return true;
228 }
229
230 [[nodiscard]] bool handle_on_string( json_value jv ) {
231 auto sv = std::string_view( );
232#if defined( DAW_USE_EXCEPTIONS )
233 try {
234#endif
235 sv = jv.get_string_view( );
236#if defined( DAW_USE_EXCEPTIONS )
237 } catch( json_exception const & ) {
238 parse_stack.push_back( state );
239 return false;
240 }
241#endif
242 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
243 parse_stack.push_back( state );
244 return false;
245 }
246 return true;
247 }
248
249 [[nodiscard]] bool handle_on_null( json_value jv ) {
250 auto sv = std::string_view( );
251#if defined( DAW_USE_EXCEPTIONS )
252 try {
253#endif
254 sv = jv.get_string_view( );
255#if defined( DAW_USE_EXCEPTIONS )
256 } catch( json_exception const & ) {
257 parse_stack.push_back( state );
258 return false;
259 }
260#endif
261 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
262 parse_stack.push_back( state );
263 return false;
264 }
265 return true;
266 }
267 } handler{ doc_start, parse_location + 1 };
268
269#if defined( DAW_USE_EXCEPTIONS )
270 try {
271#endif
272 json_event_parser( doc_start, handler );
273#if defined( DAW_USE_EXCEPTIONS )
274 } catch( json_exception const & ) {
275 // Ignoring because we are only looking for the stack leading up to
276 // this and it may have come from an error
277 }
278#endif
279 if( handler.last_popped ) {
280 handler.parse_stack.push_back( *handler.last_popped );
281 }
282 return std::move( handler.parse_stack );
283 }
284
285 [[nodiscard]] inline std::vector<json_path_node>
286 find_json_path_stack_to( json_exception const &jex,
287 char const *doc_start ) {
288 return find_json_path_stack_to( jex.parse_location( ), doc_start );
289 }
290
291 [[nodiscard]] inline std::string
292 find_json_path_to( char const *parse_location, char const *doc_start ) {
293 return to_json_path_string(
294 find_json_path_stack_to( parse_location, doc_start ) );
295 }
296
297 [[nodiscard]] inline std::string
298 find_json_path_to( json_exception const &jex, char const *doc_start ) {
299 return to_json_path_string(
300 find_json_path_stack_to( jex.parse_location( ), doc_start ) );
301 }
302
303 [[nodiscard]] constexpr std::size_t
304 find_line_number_of( char const *doc_pos, char const *doc_start ) {
305 daw_json_ensure( doc_pos != nullptr and doc_start != nullptr,
306 ErrorReason::UnexpectedEndOfData );
307 daw_json_ensure( json_details::less{ }( doc_start, doc_pos ),
308 ErrorReason::UnexpectedEndOfData );
309
310 return daw::algorithm::accumulate( doc_start, doc_pos, std::size_t{ },
311 []( std::size_t count, char c )
313 if( c == '\n' ) {
314 return count + 1;
315 }
316 return count;
317 } );
318 }
319
320 [[nodiscard]] constexpr std::size_t
321 find_line_number_of( json_path_node const &node, char const *doc_start ) {
322 return find_line_number_of( node.value_start( ), doc_start );
323 }
324
325 [[nodiscard]] constexpr std::size_t
326 find_column_number_of( char const *doc_pos, char const *doc_start ) {
327 daw_json_ensure( doc_pos != nullptr and doc_start != nullptr,
328 ErrorReason::UnexpectedEndOfData );
329 daw_json_ensure( json_details::less{ }( doc_start, doc_pos ),
330 ErrorReason::UnexpectedEndOfData );
331
332 auto const first = daw::reverse_iterator<char const *>( doc_pos );
333 auto const last = daw::reverse_iterator<char const *>( doc_start );
334 auto const pos = daw::algorithm::find( first, last, '\n' ) - first;
335 daw_json_ensure( pos >= 0, ErrorReason::Unknown );
336 return static_cast<std::size_t>( pos );
337 }
338
339 [[nodiscard]] constexpr std::size_t
340 find_column_number_of( json_path_node const &node, char const *doc_start ) {
341 return find_column_number_of( node.value_start( ), doc_start );
342 }
343 } // namespace DAW_JSON_VER
344} // namespace daw::json
constexpr json_path_node(JsonBaseParseTypes Type, std::string_view Name, long long Index, char const *ValueStart)
constexpr std::string_view name() const
The member name, only value for submembers of Class types.
constexpr char const * value_start() const
The beginning of the value's data in JSON document.
constexpr long long index() const
The element index, only valid for elements of Array types.
constexpr JsonBaseParseTypes type() const
What type of value is represented.
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
#define DAW_JSON_CPP23_STATIC_CALL_OP_DISABLE_WARNING
#define DAW_JSON_CPP23_STATIC_CALL_OP_CONST
#define DAW_JSON_CPP23_STATIC_CALL_OP_ENABLE_WARNING
#define DAW_JSON_CPP23_STATIC_CALL_OP
constexpr void json_event_parser(basic_json_value< P, A > bjv, Handler &&handler, options::parse_flags_t< ParseFlags... >)
std::string find_json_path_to(char const *parse_location, char const *doc_start)
DAW_ATTRIB_NOINLINE std::string to_json_path_string(std::vector< json_path_node > const &path_stack)
Convert a json_path_node stack to a JSON Path string.
constexpr std::size_t find_column_number_of(char const *doc_pos, char const *doc_start)
constexpr std::size_t find_line_number_of(char const *doc_pos, char const *doc_start)
std::vector< json_path_node > find_json_path_stack_to(char const *parse_location, char const *doc_start)
Get the json_path_nodes representing the path to the nearest value's position in the document.
Customization point traits.
A name/value pair of string_view/json_value.
A non-owning container for arbitrary JSON values that allows movement/iteration through.
constexpr std::string_view get_string_view() const
Construct a string range of the current value. Strings start inside the quotes.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20