DAW JSON Link
daw_json_exception.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/daw_string_view.h>
14 #include <daw/daw_traits.h>
15 #include <daw/daw_unreachable.h>
16 #include <daw/algorithms/daw_algorithm_accumulate.h>
17 
18 #include <cstddef>
19 #include <exception>
20 #include <string>
21 #include <string_view>
22 
23 namespace daw::json {
24  inline namespace DAW_JSON_VER {
25  namespace json_details {
26  struct missing_member {
27  char const *member_name;
28 
29  explicit constexpr missing_member( daw::string_view name )
30  : member_name( std::data( name ) ) {
31  if( member_name and member_name[0] == '\a' ) {
32  member_name = "no_name";
33  }
34  }
35  };
36 
37  struct missing_token {
38  char token;
39  explicit constexpr missing_token( char c )
40  : token( c ) {}
41  };
42  } // namespace json_details
43 
44  // enum class ErrorType { Unknown, MissingMember, UnexpectedCharacter };
45  enum class ErrorReason {
46  Unknown,
47  UnexpectedEndOfData,
48  InvalidNumber,
49  InvalidNumberStart,
50  InvalidNumberUnexpectedQuoting,
51  InvalidTimestamp,
52  InvalidUTFEscape,
53  InvalidUTFCodepoint,
54  InvalidEndOfValue,
55  InvalidLiteral,
56  InvalidString,
57  InvalidStringHighASCII,
58  ExpectedKeyValueToStartWithBrace,
59  ExpectedKeyValueArrayToStartWithBracket,
60  InvalidArrayStart,
61  InvalidClassStart,
62  InvalidStartOfValue,
63  InvalidTrue,
64  InvalidFalse,
65  InvalidNull,
66  UnexpectedNull,
67  NumberIsNaN,
68  NumberIsInf,
69  NumberOutOfRange,
70  EmptyJSONDocument,
71  EmptyJSONPath,
72  JSONPathNotFound,
73  InvalidJSONPath,
74  NullOutputIterator,
75  OutputError,
76  MissingMemberName,
77  InvalidMemberName,
78  ExpectedArrayOrClassStart,
79  UnknownMember,
80  InvalidBracketing,
81  AttemptToAccessPastEndOfValue,
82  OutOfOrderOrderedMembers,
83  MissingMemberNameOrEndOfClass,
84  MemberNotFound,
85  TagMemberNotFound,
86  ExpectedMemberNotFound,
87  ExpectedTokenNotFound,
88  UnexpectedJSONVariantType,
89  TrailingComma,
90  AttemptToCallOpStarOnConstIterator
91  };
92 
93  constexpr std::string_view reason_message( ErrorReason er ) {
94  using namespace std::string_view_literals;
95  switch( er ) {
96  case ErrorReason::Unknown:
97  return "Unknown reason for error"sv;
98  case ErrorReason::UnexpectedEndOfData:
99  return "Unexpected end of data"sv;
100  case ErrorReason::InvalidNumber:
101  return "Invalid Number"sv;
102  case ErrorReason::InvalidNumberStart:
103  return R"(Invalid Number started, expected a "0123456789-")"sv;
104  case ErrorReason::InvalidNumberUnexpectedQuoting:
105  return "Unexpected double quote prior to number"sv;
106  case ErrorReason::InvalidTimestamp:
107  return "Invalid Timestamp"sv;
108  case ErrorReason::InvalidUTFEscape:
109  return "Invalid UTF Escape"sv;
110  case ErrorReason::InvalidUTFCodepoint:
111  return "Invalid UTF Codepoint"sv;
112  case ErrorReason::InvalidEndOfValue:
113  return R"(Did not find \",}]" at end of value)"sv;
114  case ErrorReason::InvalidLiteral:
115  return "Literal data was corrupt"sv;
116  case ErrorReason::InvalidString:
117  return "Invalid or corrupt string"sv;
118  case ErrorReason::InvalidStringHighASCII:
119  return "String support limited to 0x20 < chr <= 0x7F when "
120  "DisallowHighEightBit or InvalidStringHighASCII is true"sv;
121  case ErrorReason::ExpectedKeyValueToStartWithBrace:
122  return "Expected key/value's JSON type to be of class type and "
123  "beginning "
124  "with '{'"sv;
125  case ErrorReason::ExpectedKeyValueArrayToStartWithBracket:
126  return "Expected key/value's JSON type to be of array type and "
127  "beginning "
128  "with '['"sv;
129  case ErrorReason::InvalidArrayStart:
130  return "Expected array type to begin with '['"sv;
131  case ErrorReason::InvalidClassStart:
132  return "Expected class type to begin with '{'"sv;
133  case ErrorReason::InvalidStartOfValue:
134  return "Unexpected character data at start of value"sv;
135  case ErrorReason::InvalidTrue:
136  return "Expected true not found"sv;
137  case ErrorReason::InvalidFalse:
138  return "Expected false not found"sv;
139  case ErrorReason::InvalidNull:
140  return "Expected null not found"sv;
141  case ErrorReason::UnexpectedNull:
142  return "An unexpected null value was encountered while serializing"sv;
143  case ErrorReason::NumberIsNaN:
144  return "NaN encountered while serializing to JSON Number literal"sv;
145  case ErrorReason::NumberIsInf:
146  return "Infinity encountered while serializing to JSON Number literal"sv;
147  case ErrorReason::NumberOutOfRange:
148  return "Number is outside of the representable range"sv;
149  case ErrorReason::EmptyJSONDocument:
150  return "Attempt to parse an empty JSON document"sv;
151  case ErrorReason::EmptyJSONPath:
152  return "Empty JSON Path specified"sv;
153  case ErrorReason::JSONPathNotFound:
154  return "JSON Path specified not found in document"sv;
155  case ErrorReason::InvalidJSONPath:
156  return "Invalid JSON Path specified"sv;
157  case ErrorReason::NullOutputIterator:
158  return "Null pointer specified for output"sv;
159  case ErrorReason::OutputError:
160  return "General error while performing output"sv;
161  case ErrorReason::MissingMemberNameOrEndOfClass:
162  return "Missing member name or end of class"sv;
163  case ErrorReason::MissingMemberName:
164  return "Missing member name"sv;
165  case ErrorReason::InvalidMemberName:
166  return "Member names must be JSON strings"sv;
167  case ErrorReason::ExpectedArrayOrClassStart:
168  return "Expected start of a JSON class or array"sv;
169  case ErrorReason::UnknownMember:
170  return "Could not find member in JSON class"sv;
171  case ErrorReason::InvalidBracketing:
172  return "Invalid Bracketing"sv;
173  case ErrorReason::AttemptToAccessPastEndOfValue:
174  return "A value of known size was accessed past the end"sv;
175  case ErrorReason::OutOfOrderOrderedMembers:
176  return "Order of ordered members must be ascending"sv;
177  case ErrorReason::MemberNotFound:
178  return "Expected member not found"sv;
179  case ErrorReason::TagMemberNotFound:
180  return "Expected tag member not found, they are required for tagged "
181  "Variants"sv;
182  case ErrorReason::ExpectedMemberNotFound:
183  return "Expected member missing"sv;
184  case ErrorReason::ExpectedTokenNotFound:
185  return "Expected token missing"sv;
186  case ErrorReason::UnexpectedJSONVariantType:
187  return "Unexpected JSON Variant Type"sv;
188  case ErrorReason::TrailingComma:
189  return "Trailing comma"sv;
190  case ErrorReason::AttemptToCallOpStarOnConstIterator:
191  return "Use of operator*( ) on const iterator";
192  }
193  DAW_UNREACHABLE( );
194  }
195 
196  /***
197  * When a parser error occurs this is thrown. It will provide the local
198  * reason for the error and some information about the location in the
199  * parser if available. Using the bool flag to ensure that the exception
200  * type matches the compiler define and has a different name
201  */
202  class json_exception : public std::exception {
203  ErrorReason m_reason = ErrorReason::Unknown;
204  union data_t {
205  char const *pointer;
206  char token;
207 
208  explicit constexpr data_t( char const *p )
209  : pointer( p ) {}
210  explicit constexpr data_t( char t )
211  : token( t ) {}
212  } m_data{ nullptr };
213  char const *m_parse_loc = nullptr;
214 
215  public:
216  explicit json_exception( ) = default;
217 
218  explicit inline json_exception( ErrorReason reason )
219  : m_reason( reason ) {}
220 
221  explicit inline json_exception( json_details::missing_member mm )
222  : m_reason( ErrorReason::MemberNotFound )
223  , m_data( mm.member_name ) {}
224 
225  explicit inline json_exception( json_details::missing_token mt )
226  : m_reason( ErrorReason::ExpectedTokenNotFound )
227  , m_data( mt.token ) {}
228 
229  explicit inline json_exception( json_details::missing_member mm,
230  std::string_view location )
231  : m_reason( ErrorReason::MemberNotFound )
232  , m_data( mm.member_name )
233  , m_parse_loc( std::data( location ) ) {}
234 
235  explicit inline json_exception( json_details::missing_token mt,
236  char const *location )
237  : m_reason( ErrorReason::ExpectedTokenNotFound )
238  , m_data( mt.token )
239  , m_parse_loc( location ) {}
240 
241  explicit inline json_exception( ErrorReason reason, char const *location )
242  : m_reason( reason )
243  , m_parse_loc( location ) {}
244 
245  [[nodiscard]] inline ErrorReason reason_type( ) const {
246  return m_reason;
247  }
248 
249  DAW_ATTRIB_NOINLINE [[nodiscard]] inline std::string reason( ) const {
250 #if defined( DAW_HAS_CLANG )
251 #pragma clang diagnostic push
252 #pragma clang diagnostic ignored "-Wswitch-enum"
253 #endif
254  switch( m_reason ) {
255  case ErrorReason::MemberNotFound: {
256  using namespace std::string_literals;
257  return "Could not find required class member '"s +
258  static_cast<std::string>( m_data.pointer ) + "'"s;
259  }
260  case ErrorReason::ExpectedTokenNotFound: {
261  using namespace std::string_literals;
262  return "Could not find expected parse token '"s + m_data.token + "'"s;
263  }
264  default:
265  return std::string( ( reason_message( m_reason ) ) );
266  }
267 #if defined( DAW_HAS_CLANG )
268 #pragma clang diagnostic pop
269 #endif
270  }
271 
272  DAW_ATTRIB_NOINLINE [[nodiscard]] constexpr char const *
273  parse_location( ) const {
274  return m_parse_loc;
275  }
276  DAW_ATTRIB_INLINE char const *what( ) const noexcept override {
277  // reason_message returns a string_view to a literal
278  return reason_message( m_reason ).data( );
279  }
280  DAW_ATTRIB_INLINE json_exception( json_exception const & ) = default;
281  DAW_ATTRIB_INLINE json_exception( json_exception && ) noexcept = default;
282  DAW_ATTRIB_INLINE json_exception &
283  operator=( json_exception const & ) = default;
284  DAW_ATTRIB_INLINE json_exception &
285  operator=( json_exception && ) noexcept = default;
286  DAW_ATTRIB_INLINE ~json_exception( ) override = default;
287  };
288 
289  /***
290  * Helper to provide output formatted information about json_exception
291  * @param je json_exception to be formatted
292  * @return string representation of json_exception
293  */
294  DAW_ATTRIB_NOINLINE [[nodiscard]] inline std::string
295  to_formatted_string( json_exception const &je,
296  char const *json_document = nullptr ) {
297  using namespace std::string_literals;
298  std::string result = "reason: "s + je.reason( );
299  if( json_document == nullptr or je.parse_location( ) == nullptr ) {
300  return result;
301  }
302  char const *last_nl = nullptr;
303  auto const line_no = daw::algorithm::accumulate(
304  json_document, je.parse_location( ), std::size_t{ 1 },
305  [&]( std::size_t count, char const &c ) {
306  if( c == '\n' ) {
307  last_nl = &c;
308  ++count;
309  }
310  return count;
311  } );
312  auto const col_no =
313  static_cast<std::size_t>( je.parse_location( ) - last_nl ) + 1U;
314  auto const previous_char_count =
315  ( std::min )( static_cast<std::size_t>( 50 ),
316  static_cast<std::size_t>( std::distance(
317  json_document, je.parse_location( ) + 1 ) ) );
318  auto const loc_data = std::string_view(
319  std::prev( je.parse_location( ),
320  static_cast<std::ptrdiff_t>( previous_char_count ) ),
321  previous_char_count + 1 );
322  result += " \nlocation: near line: " + std::to_string( line_no ) +
323  " col: " + std::to_string( col_no ) + "\n\"";
324 #if not defined( DAW_JSON_NO_COLOUR )
325  result += "\x1b[1m";
326 #endif
327  result.reserve( result.size( ) + std::size( loc_data ) );
328  result += daw::algorithm::accumulate(
329  std::data( loc_data ), daw::data_end( loc_data ), std::string{ },
330  []( std::string s, char c ) {
331  switch( c ) {
332  case '\n':
333  case '\r':
334  break;
335 #if defined( DAW_JSON_NO_COLOUR )
336  case '"':
337  s += '\\';
338  [[fallthrough]];
339 #endif
340  default:
341  s += c;
342  break;
343  }
344  return s;
345  } );
346 #if not defined( DAW_JSON_NO_COLOUR )
347  result += "\x1b[0m";
348 #endif
349  result += "\"\n";
350  return result;
351  }
352  } // namespace DAW_JSON_VER
353 } // namespace daw::json
constexpr std::string_view to_string(JsonBaseParseTypes pt)
@ Unknown
Array - An array type where each element is mapped to the member of a C++ class.
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25