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