1 /++
2 Mir Ion error codes, messages, and exceptions.
3 +/
4 module mir.ion.exception;
5 
6 import mir.array.allocation: array;
7 import mir.ndslice.topology: map;
8 import mir.serde: SerdeException;
9 import std.traits: EnumMembers, Unqual;
10 
11 /++
12 Ion Error Codes
13 +/
14 enum IonErrorCode
15 {
16     ///
17     none,
18     ///
19     nop,
20     ///
21     jsonUnexpectedValue,
22     ///
23     jsonUnexpectedEnd,
24     ///
25     symbolTableCantInsertKey,
26     ///
27     illegalTypeDescriptor,
28     ///
29     unexpectedEndOfData,
30     ///
31     unexpectedIonType,
32     ///
33     overflowInParseVarUInt,
34     ///
35     overflowInParseVarInt,
36     ///
37     overflowInIntegerValue,
38     ///
39     overflowInDecimalValue,
40     ///
41     overflowInSymbolId,
42     ///
43     zeroAnnotations,
44     ///
45     illegalBinaryData,
46     ///
47     illegalTimeStamp,
48     ///
49     wrongBoolDescriptor,
50     ///
51     wrongIntDescriptor,
52     ///
53     wrongFloatDescriptor,
54     ///
55     nullBool,
56     ///
57     nullInt,
58     ///
59     nullFloat,
60     ///
61     nullTimestamp,
62     ///
63     expectedNullValue,
64     ///
65     expectedBoolValue,
66     ///
67     expectedIntegerValue,
68     ///
69     expectedFloatingValue,
70     ///
71     expectedEnumValue,
72     ///
73     expectedStringValue,
74     ///
75     expectedCharValue,
76     ///
77     expectedStructValue,
78     ///
79     expectedListValue,
80     ///
81     expectedTimestampValue,
82     ///
83     requiredDefaultClassConstructor,
84     ///
85     integerOverflow,
86     ///
87     smallStringOverflow,
88     ///
89     smallArrayOverflow,
90     ///
91     unexpectedVersionMarker,
92     ///
93     cantParseValueStream,
94     ///
95     symbolIdIsTooLargeForTheCurrentSymbolTable,
96     ///
97     invalidLocalSymbolTable,
98     ///
99     sharedSymbolTablesAreUnsupported,
100     ///
101     unableToOpenFile,
102     ///
103     eof,
104     ///
105     errorReadingFile,
106     ///
107     errorReadingStream,
108     ///
109     tooManyElementsForStaticArray,
110     ///
111     notEnoughElementsForStaticArray,
112     ///
113     unusedAnnotations,
114     ///
115     missingAnnotation,
116     ///
117     cantConvertAnnotationToEnum,
118     ///
119     nestedAnnotationsNotAllowed,
120     ///
121     expectedIonStructForAnAssociativeArrayDeserialization,
122     ///
123     unexpectedComma,
124     ///
125     emptyIonInput,
126     ///
127     emptyOrderedStruct,
128     ///
129     negativeIntegerZero,
130 }
131 
132 ///
133 version(mir_ion_test) unittest
134 {
135     static assert(!IonErrorCode.none);
136     static assert(IonErrorCode.none == IonErrorCode.init);
137     static assert(IonErrorCode.nop > 0);
138 }
139 
140 /++
141 Params:
142     code = $(LREF IonErrorCode)
143 Returns:
144     corresponding error message
145 +/
146 string ionErrorMsg()(IonErrorCode code) @property
147 @safe pure nothrow @nogc
148 {
149     static immutable string[] msgs = [
150         null,
151         "unexpected NOP Padding",
152         "unexpected JSON value",
153         "unexpected JSON end",
154         "symbol table can't insert key",
155         "illegal type descriptor",
156         "unexpected end of data",
157         "unexpected Ion type",
158         "overflow in parseVarUInt",
159         "overflow in parseVarInt",
160         "overflow in integer value",
161         "overflow in decimal value",
162         "overflow in symbol id",
163         "at least one annotation is required",
164         "illegal binary data",
165         "illegal timestamp",
166         "wrong bool descriptor",
167         "wrong int descriptor",
168         "wrong float descriptor",
169         "null bool",
170         "null int",
171         "null float",
172         "null timestamp",
173         "expected null value",
174         "expected boolean value",
175         "expected integer point value",
176         "expected floating value",
177         "expected enum value",
178         "expected string value",
179         "expected char value",
180         "expected struct value",
181         "expected list value",
182         "expected timestamp value",
183         "required default class constructor",
184         "integer overflow",
185         "small string overflow",
186         "small array overflow",
187         "unexpected version marker",
188         "can't parse value stream",
189         "symbol id is too large for the current symbol table",
190         "invalid local symbol table",
191         "shared symbol tables are unsupported",
192         "unable to open file",
193         "end of file",
194         "error reading file",
195         "error reading stream",
196         "too many elements for static array",
197         "not enough elements for static array",
198         "unused annotations",
199         "missing annotation",
200         "can't convert annotation to enum",
201         "nested annotations aren't allowed",
202         "expected IonStruct for an associative array deserialization",
203         "unexpected comma",
204         "Ion data doesn't contain a value",
205         "empty ordered struct",
206         "negative integer zero",
207     ];
208     return msgs[code - IonErrorCode.min];
209 }
210 
211 ///
212 @safe pure nothrow @nogc
213 version(mir_ion_test) unittest
214 {
215     static assert(IonErrorCode.nop.ionErrorMsg == "unexpected NOP Padding", IonErrorCode.nop.ionErrorMsg);
216     static assert(IonErrorCode.none.ionErrorMsg is null);
217 }
218 
219 version (D_Exceptions):
220 
221 /++
222 Mir Ion Exception Class
223 +/
224 class IonException : SerdeException
225 {
226     ///
227     this(
228         string msg,
229         string file = __FILE__,
230         size_t line = __LINE__,
231         Throwable next = null) pure nothrow @nogc @safe 
232     {
233         super(msg, file, line, next);
234     }
235 
236     ///
237     this(
238         string msg,
239         Throwable next,
240         string file = __FILE__,
241         size_t line = __LINE__,
242         ) pure nothrow @nogc @safe 
243     {
244         this(msg, file, line, next);
245     }
246 }
247 
248 /++
249 Mir Ion Exception Class
250 +/
251 class IonMirException : IonException
252 {
253     import mir.exception: MirThrowableImpl, mirExceptionInitilizePayloadImpl;
254     private enum maxMsgLen = 447;
255     ///
256     mixin MirThrowableImpl;
257 }
258 
259 /++
260 Mir Ion Parser Exception Class
261 
262 The exception is used for JSON parsing.
263 +/
264 class IonParserMirException : IonMirException
265 {
266     ///
267     size_t location;
268 
269     ///
270     this(
271         scope const(char)[] msg,
272         size_t location,
273         string file = __FILE__,
274         size_t line = __LINE__,
275         Throwable next = null) pure nothrow @nogc @safe 
276     {
277         this.location = location;
278         import mir.format: stringBuf, getData;
279         super(stringBuf() << msg << ". location = " << location << getData, file, line, next);
280     }
281 
282     ///
283     this(
284         scope const(char)[] msg,
285         size_t location,
286         Throwable next,
287         string file = __FILE__,
288         size_t line = __LINE__,
289         ) pure nothrow @nogc @safe 
290     {
291         this(msg, location, file, line, next);
292     }
293 }
294 
295 private static immutable IonException[] exceptionsArray =
296     [EnumMembers!IonErrorCode]
297     .map!(code => code ? new IonException("IonException: " ~ code.ionErrorMsg) : null)
298     .array;
299 
300 /++
301 Params:
302     code = $(LREF IonErrorCode)
303 Returns:
304     $(LREF IonException)
305 +/
306 IonException ionException(IonErrorCode code) @property
307 @trusted pure nothrow @nogc
308 {
309     pragma(inline, true);
310     return cast(IonException) exceptionsArray[code - IonErrorCode.min];
311 }
312 
313 ///
314 @safe pure nothrow @nogc
315 version(mir_ion_test) unittest
316 {
317     static assert(IonErrorCode.nop.ionException.msg == "IonException: unexpected NOP Padding", IonErrorCode.nop.ionException.msg);
318     static assert(IonErrorCode.none.ionException is null);
319 }
320 
321 package(mir) Unqual!T unqualException(T)(return T exception) @trusted pure nothrow @nogc
322     // if (is(T : const Exception))
323 {
324     return cast() exception;
325 }