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