DAW JSON Link
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 
13 #include "daw_json_event_parser.h"
14 #include "daw_json_link_types.h"
15 #include "impl/daw_json_assert.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 
28 namespace 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 char const * value_start() const
The beginning of the value's data in JSON document.
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
This is in addition to the parse policy. Always do a full name match instead of sometimes relying on ...
constexpr std::string_view to_string(JsonBaseParseTypes pt)
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)
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.
constexpr std::size_t find_line_number_of(char const *doc_pos, char const *doc_start)
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:25