1 /**
2    Miscelleanous functions that assist in type conversions.
3  */
4 module xlld.conv.misc;
5 
6 import xlld.from;
7 
8 ///
9 template isUserStruct(T) {
10     import xlld.any: Any;
11     import xlld.sdk.xlcall: XLOPER12;
12     import std.datetime: DateTime;
13     import std.typecons: Tuple;
14     import std.traits: Unqual;
15     import std.range.primitives: isInputRange;
16 
17     enum isUserStruct =
18         is(T == struct)
19         && !is(Unqual!T == Any)
20         && !is(Unqual!T == DateTime)
21         && !is(Unqual!T: Tuple!A, A...)
22         && !is(Unqual!T == XLOPER12)
23         && !isVector!T
24         && !isInputRange!T
25         ;
26 }
27 
28 template isVector(T) {
29     version(Have_automem) {
30         import automem.vector: Vector;
31         import std.traits: Unqual;
32         enum isVector = is(Unqual!T == Vector!(E,  A), E, A);
33     } else
34         enum isVector = false;
35 }
36 
37 
38 template isSomeString(T) {
39     import std.traits: isSomeString_ = isSomeString, Unqual;
40     enum isSomeString =
41         isSomeString_!T
42         || (isVector!T && is(Unqual!(typeof(T.init[0])) == char))
43         ;
44 }
45 
46 
47 template isTuple(T) {
48     import std.typecons: Tuple;
49     import std.traits: Unqual;
50     enum isTuple = is(Unqual!T: Tuple!A, A...);
51 }
52 
53 
54 template isSequence(T) {
55     import std.traits: isArray;
56     enum isSequence = isArray!T || isTuple!T || isVector!T;
57 }
58 
59 ///
60 __gshared immutable gDupMemoryException = new Exception("Failed to allocate memory in dup");
61 
62 
63 /**
64    Deep copy of an oper
65  */
66 from!"xlld.sdk.xlcall".XLOPER12 dup(A)(from!"xlld.sdk.xlcall".XLOPER12 oper, ref A allocator) @safe {
67 
68     import xlld.sdk.xlcall: XLOPER12, XlType;
69     import std.experimental.allocator: makeArray;
70 
71     XLOPER12 ret;
72 
73     ret.xltype = oper.xltype;
74 
75     switch(stripMemoryBitmask(oper.xltype)) with(XlType) {
76 
77         default:
78             ret = oper;
79             return ret;
80 
81         case xltypeStr:
82             const length = operStringLength(oper) + 1;
83 
84             () @trusted {
85                 ret.val.str = allocator.makeArray!wchar(length).ptr;
86                 if(ret.val.str is null)
87                     throw gDupMemoryException;
88             }();
89 
90             () @trusted { ret.val.str[0 .. length] = oper.val.str[0 .. length]; }();
91             return ret;
92 
93         case xltypeMulti:
94             () @trusted {
95                 ret.val.array.rows = oper.val.array.rows;
96                 ret.val.array.columns = oper.val.array.columns;
97                 const length = oper.val.array.rows * oper.val.array.columns;
98                 ret.val.array.lparray = allocator.makeArray!XLOPER12(length).ptr;
99 
100                 if(ret.val.array.lparray is null)
101                     throw gDupMemoryException;
102 
103                 foreach(i; 0 .. length) {
104                     ret.val.array.lparray[i] = oper.val.array.lparray[i].dup(allocator);
105                 }
106             }();
107 
108             return ret;
109     }
110 
111     assert(0);
112 }
113 
114 
115 
116 from!"xlld.sdk.xlcall".XlType stripMemoryBitmask(in from!"xlld.sdk.xlcall".XlType type) @safe @nogc pure nothrow {
117     import xlld.sdk.xlcall: XlType, xlbitXLFree, xlbitDLLFree;
118     return cast(XlType)(type & ~(xlbitXLFree | xlbitDLLFree));
119 }
120 
121 ///
122 ushort operStringLength(T)(in T value) {
123     import xlld.sdk.xlcall: XlType;
124     import nogc.exception: enforce;
125 
126     // nogc.enforce is @system
127     () @trusted {
128         enforce(value.xltype.stripMemoryBitmask == XlType.xltypeStr,
129                 "Cannot calculate string length for oper of type ", cast(int) value.xltype);
130     }();
131 
132     // @trusted because of the `enforce` above
133     return () @trusted { return cast(ushort) value.val.str[0]; }();
134 }
135 
136 
137 // can't be pure because to!double isn't pure
138 string toString(in from!"xlld.sdk.xlcall".XLOPER12 oper) @safe {
139     import xlld.sdk.xlcall: XlType;
140     import xlld.conv.misc: stripMemoryBitmask;
141     import std.conv: text;
142     import std.format: format;
143 
144     string ret;
145 
146     ret ~= "XLOPER12(";
147     switch(stripMemoryBitmask(oper.xltype)) {
148     default:
149         ret ~= oper.xltype.stripMemoryBitmask.text;
150         break;
151 
152     case XlType.xltypeSRef:
153         import xlld.func.xl: Coerced;
154         auto coerced = () @trusted { return Coerced(&oper); }();
155         return "SRef[ " ~ coerced.toString ~ " ]";
156 
157     case XlType.xltypeNum:
158         ret ~= format!"%.6f"(oper.val.num);
159         break;
160 
161     case XlType.xltypeStr:
162         ret ~= `"`;
163         () @trusted {
164             const ulong length = oper.val.str[0];
165             ret ~= text(oper.val.str[1 .. 1 + length]);
166         }();
167         ret ~= `"`;
168         break;
169 
170     case XlType.xltypeInt:
171         ret ~= text(oper.val.w);
172         break;
173 
174     case XlType.xltypeBool:
175         ret ~= text(oper.val.bool_);
176         break;
177 
178     case XlType.xltypeErr:
179         ret ~= "ERROR";
180         break;
181 
182     case XlType.xltypeBigData:
183         () @trusted {
184             ret ~= "BIG(";
185             ret ~= text(oper.val.bigdata.h.hdata);
186             ret ~= ", ";
187             ret ~= text(oper.val.bigdata.cbData);
188             ret ~= ")";
189         }();
190     }
191     ret ~= ")";
192     return ret;
193 }
194 
195 ///
196 __gshared immutable multiMemoryException = new Exception("Failed to allocate memory for multi oper");
197 
198 /// Returns an array XLOPER12
199 from!"xlld.sdk.xlcall".XLOPER12 multi(A)(int rows, int cols, ref A allocator) @trusted {
200     import xlld.sdk.xlcall: XLOPER12, XlType;
201 
202     auto ret = XLOPER12();
203 
204     ret.xltype = XlType.xltypeMulti;
205     () @trusted {
206         ret.val.array.rows = rows;
207         ret.val.array.columns = cols;
208     }();
209 
210     auto slice = allocator.allocate(rows * cols * ret.sizeof);
211     ret.val.array.lparray = () @trusted { return cast(XLOPER12*) slice.ptr; }();
212 
213     if(ret.val.array.lparray is null)
214         throw multiMemoryException;
215 
216     return ret;
217 }