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