1 /++
2 $(H4 High level Ion deserialization API)
3 
4 Macros:
5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP)
6 +/
7 module mir.deser.ion;
8 
9 private alias AliasSeq(T...) = T;
10 
11 /++
12 +/
13 template deserializeIon(T, bool annotated = false)
14 {
15     import mir.ion.value: IonDescribedValue, IonAnnotations;
16 
17     static if (annotated)
18         alias OptIonAnnotations = IonAnnotations;
19     else
20         alias OptIonAnnotations = AliasSeq!();
21 
22     /++
23     +/
24     T deserializeIon()(scope const char[][] symbolTable, scope IonDescribedValue ionValue, scope OptIonAnnotations optionalAnnotations)
25     {
26         T value;
27         deserializeIon(value, symbolTable, ionValue, optionalAnnotations);
28         return value;
29     }
30 
31     /// ditto
32     void deserializeIon()(
33         scope ref T value, scope const char[][] symbolTable,
34         scope IonDescribedValue ionValue,
35         scope OptIonAnnotations optionalAnnotations)
36     {
37         import std.meta: anySatisfy;
38         import mir.algebraic: Algebraic;
39         import std.traits: isDynamicArray;
40     static if (is(immutable T : immutable Algebraic!TypeSet, TypeSet...)
41         && anySatisfy!(isDynamicArray, Algebraic!TypeSet.AllowedTypes))
42     {
43         import mir.ndslice.topology: map;
44         import mir.array.allocation: array;
45         deserializeIon(value, symbolTable.map!idup.array, ionValue, optionalAnnotations);
46     }
47     else
48     {
49         import mir.appender: scopedBuffer;
50         import mir.deser: hasDeserializeFromIon, deserializeValue, TableKind;
51         import mir.serde: serdeGetDeserializationKeysRecurse;
52         import mir.string_table: createTable;
53 
54         static if (hasDeserializeFromIon!T)
55             enum keys = string[].init;
56         else
57             enum keys = serdeGetDeserializationKeysRecurse!T;
58 
59         alias createTableChar = createTable!char;
60         static immutable table = createTableChar!(keys, false);
61 
62         auto tableMapBuffer = scopedBuffer!(uint, 1024);
63         foreach (key; symbolTable)
64         {
65             uint id;
66             if (!table.get(key, id))
67                 id = uint.max;
68             tableMapBuffer.put(id);
69         }
70         if (auto exception = deserializeValue!(keys, TableKind.scopeRuntime)(ionValue, value, symbolTable, tableMapBuffer.data, optionalAnnotations))
71             throw (() @trusted => cast() exception)();
72     }}
73 
74     /// ditto
75     // the same code with GC allocated symbol table
76     T deserializeIon(const string[] symbolTable, scope IonDescribedValue ionValue, scope OptIonAnnotations optionalAnnotations)
77     {
78         T value;
79         deserializeIon(value, symbolTable, ionValue, optionalAnnotations);
80         return value;
81     }
82 
83     /// ditto
84     void deserializeIon()(scope ref T value, const string[] symbolTable, scope IonDescribedValue ionValue, scope OptIonAnnotations optionalAnnotations)
85     {
86         import mir.appender: scopedBuffer;
87         import mir.deser: hasDeserializeFromIon, deserializeValue, TableKind;
88         import mir.serde: serdeGetDeserializationKeysRecurse;
89         import mir.string_table: MirStringTable;
90 
91         static if (hasDeserializeFromIon!T)
92             static immutable keys = string[].init;
93         else
94             static immutable keys = serdeGetDeserializationKeysRecurse!T;
95 
96         alias Table = MirStringTable!(keys.length, keys.length ? keys[$ - 1].length : 0);
97 
98         static if (keys.length)
99             static immutable table = Table(keys[0 .. keys.length]);
100         else
101             static immutable table = Table.init;
102 
103         auto tableMapBuffer = scopedBuffer!(uint, 1024);
104 
105         foreach (key; symbolTable)
106         {
107             uint id;
108             if (!table.get(key, id))
109                 id = uint.max;
110             tableMapBuffer.put(id);
111         }
112 
113         if (auto exception = deserializeValue!(keys, TableKind.immutableRuntime)(ionValue, value, symbolTable, tableMapBuffer.data, optionalAnnotations))
114             throw exception;            
115     }
116 
117     static if (!annotated)
118     {
119         /++
120         +/
121         T deserializeIon()(scope const(ubyte)[] data)
122         {
123             T value;
124             deserializeIon(value, data);
125             return value;
126         }
127 
128         /// ditto
129         void deserializeIon()(scope ref T value, scope const(ubyte)[] data)
130         {
131             import mir.ion.stream: IonValueStream;
132             import mir.ion.exception: IonErrorCode, ionException;
133 
134             foreach (symbolTable, scope ionValue; data.IonValueStream)
135             {
136                 return .deserializeIon!T(value, symbolTable, ionValue);
137             }
138 
139             throw IonErrorCode.emptyIonInput.ionException;
140         }
141     }
142 }
143 
144 version(mir_ion_test)
145 unittest
146 {
147     alias d = deserializeIon!(int[string]);
148     import mir.ion.value;
149     enum EEE { a, b }
150     alias deserNull = deserializeIon!EEE;
151 }