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