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
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
23namespace 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
@ 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:20