1 /**
2    Conversions from D types to XLOPER12
3  */
4 module xlld.conv.to;
5 
6 import xlld.from;
7 import xlld.conv.misc: isUserStruct;
8 import xlld.sdk.xlcall: XLOPER12, XlType;
9 import xlld.any: Any;
10 import xlld.wrap.wrap: isWantedType;
11 import std.traits: isIntegral, Unqual;
12 import std.datetime: DateTime;
13 import std.typecons: Tuple;
14 
15 alias FromEnumConversionFunction = string delegate(int) @safe;
16 package __gshared FromEnumConversionFunction[string] gFromEnumConversions;
17 shared from!"core.sync.mutex".Mutex gFromEnumMutex;
18 
19 
20 ///
21 XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(isIntegral!T) {
22     import xlld.sdk.xlcall: XlType;
23 
24     auto ret = XLOPER12();
25     ret.xltype = XlType.xltypeInt;
26     ret.val.w = val;
27 
28     return ret;
29 }
30 
31 
32 ///
33 XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(is(Unqual!T == double)) {
34     import xlld.sdk.xlcall: XlType;
35 
36     auto ret = XLOPER12();
37     ret.xltype = XlType.xltypeNum;
38     ret.val.num = val;
39 
40     return ret;
41 }
42 
43 
44 ///
45 __gshared immutable toXlOperMemoryException = new Exception("Failed to allocate memory for string oper");
46 
47 
48 ///
49 XLOPER12 toXlOper(T, A)(in T val, ref A allocator)
50     if(is(Unqual!T == string) || is(Unqual!T == wstring))
51 {
52     import xlld.sdk.xlcall: XCHAR;
53     import std.utf: byWchar;
54 
55     const numBytes = numOperStringBytes(val);
56     auto wval = () @trusted { return cast(wchar[])allocator.allocate(numBytes); }();
57     if(wval is null)
58         throw toXlOperMemoryException;
59 
60     int i = 1;
61     foreach(ch; val.byWchar) {
62         wval[i++] = ch;
63     }
64 
65     wval[0] = cast(ushort)(i - 1);
66 
67     auto ret = XLOPER12();
68     ret.xltype = XlType.xltypeStr;
69     () @trusted { ret.val.str = cast(XCHAR*)&wval[0]; }();
70 
71     return ret;
72 }
73 
74 
75 
76 /// the number of bytes required to store `str` as an XLOPER12 string
77 package size_t numOperStringBytes(T)(in T str) if(is(Unqual!T == string) || is(Unqual!T == wstring)) {
78     // XLOPER12 strings are wide strings where index 0 is the length
79     // and [1 .. $] is the actual string
80     return (str.length + 1) * wchar.sizeof;
81 }
82 
83 
84 ///
85 XLOPER12 toXlOper(T, A)(T[] values, ref A allocator)
86     if(isWantedType!T && (!is(T: E[], E) || is(Unqual!T == string)))
87 {
88     T[][1] realValues = [values];
89     return realValues[].toXlOper(allocator);
90 }
91 
92 
93 ///
94 __gshared immutable toXlOperShapeException = new Exception("# of columns must all be the same and aren't");
95 
96 ///
97 XLOPER12 toXlOper(T, A)(T[][] values, ref A allocator)
98     if(isWantedType!T && (!is(T: E[], E) || is(Unqual!T == string)))
99 {
100     import xlld.conv.misc: multi;
101     import std.algorithm: map, all;
102     import std.array: array;
103 
104     if(!values.all!(a => a.length == values[0].length))
105        throw toXlOperShapeException;
106 
107     const rows = cast(int)values.length;
108     const cols = values.length ? cast(int)values[0].length : 0;
109     auto ret = multi(rows, cols, allocator);
110     auto opers = () @trusted { return ret.val.array.lparray[0 .. rows*cols]; }();
111 
112     int i;
113     foreach(ref row; values) {
114         foreach(ref val; row) {
115             opers[i++] = val.toXlOper(allocator);
116         }
117     }
118 
119     return ret;
120 }
121 
122 
123 ///
124 XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == Any)) {
125     import xlld.conv.misc: dup;
126     return value.dup(allocator);
127 }
128 
129 
130 XLOPER12 toXlOper(T, A)(T value, ref A allocator, in string file = __FILE__, in size_t line = __LINE__)
131     @safe if(is(Unqual!T == DateTime))
132 {
133     import xlld.sdk.framework: Excel12f;
134     import xlld.sdk.xlcall: xlfDate, xlfTime, xlretSuccess;
135     import nogc.conv: text;
136 
137     XLOPER12 ret, date, time;
138 
139     auto year = value.year.toXlOper(allocator);
140     auto month = () @trusted { return toXlOper(cast(int)value.month, allocator); }();
141     auto day = value.day.toXlOper(allocator);
142 
143     () @trusted {
144         assert(year.xltype == XlType.xltypeInt, text("year is not int but ", year.xltype));
145         assert(month.xltype == XlType.xltypeInt, text("month is not int but ", month.xltype));
146         assert(day.xltype == XlType.xltypeInt, text("day is not int but ", day.xltype));
147     }();
148 
149     const dateCode = () @trusted { return Excel12f(xlfDate, &date, &year, &month, &day); }();
150     if(dateCode != xlretSuccess)
151         throw new Exception("Error calling xlfDate", file, line);
152     () @trusted { assert(date.xltype == XlType.xltypeNum, text("date is not xltypeNum but ", date.xltype)); }();
153 
154     auto hour = value.hour.toXlOper(allocator);
155     auto minute = value.minute.toXlOper(allocator);
156     auto second = value.second.toXlOper(allocator);
157 
158     const timeCode = () @trusted { return Excel12f(xlfTime, &time, &hour, &minute, &second); }();
159     if(timeCode != xlretSuccess)
160         throw new Exception("Error calling xlfTime", file, line);
161 
162     () @trusted { assert(time.xltype == XlType.xltypeNum, text("time is not xltypeNum but ", time.xltype)); }();
163 
164     ret.xltype = XlType.xltypeNum;
165     ret.val.num = date.val.num + time.val.num;
166     return ret;
167 }
168 
169 
170 XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == bool)) {
171     import xlld.sdk.xlcall: XlType;
172     XLOPER12 ret;
173     ret.xltype = XlType.xltypeBool;
174     ret.val.bool_ = cast(typeof(ret.val.bool_)) value;
175     return ret;
176 }
177 
178 /**
179    Register a custom conversion from enum (going through integer) to a string.
180    This function will be called to convert enum return values from wrapped
181    D functions into strings in Excel.
182  */
183 void registerConversionFrom(T)(FromEnumConversionFunction func) @trusted {
184     import std.traits: fullyQualifiedName;
185 
186     assert(gFromEnumMutex !is null, "gFromEnumMutex is null");
187 
188     gFromEnumMutex.lock_nothrow;
189     scope(exit)gFromEnumMutex.unlock_nothrow;
190 
191     gFromEnumConversions[fullyQualifiedName!T] = func;
192 }
193 
194 
195 void unregisterConversionFrom(T)() @trusted {
196     import std.traits: fullyQualifiedName;
197 
198     assert(gFromEnumMutex !is null, "gFromEnumMutex is null");
199 
200     gFromEnumMutex.lock_nothrow;
201     scope(exit)gFromEnumMutex.unlock_nothrow;
202 
203     gFromEnumConversions.remove(fullyQualifiedName!T);
204 }
205 
206 
207 
208 
209 XLOPER12 toXlOper(T, A)(T value, ref A allocator) @trusted if(is(T == enum)) {
210 
211     import std.conv: text;
212     import std.traits: fullyQualifiedName;
213     import core.memory: GC;
214 
215     enum name = fullyQualifiedName!T;
216 
217     {
218         assert(gFromEnumMutex !is null, "gFromEnumMutex is null");
219 
220         gFromEnumMutex.lock_nothrow;
221         scope(exit) gFromEnumMutex.unlock_nothrow;
222 
223         if(name in gFromEnumConversions)
224             return gFromEnumConversions[name](value).toXlOper(allocator);
225     }
226 
227     scope str = text(value);
228     auto ret = str.toXlOper(allocator);
229     () @trusted { GC.free(cast(void*)str.ptr); }();
230 
231     return ret;
232 }
233 
234 XLOPER12 toXlOper(T, A)(T value, ref A allocator)
235     if(isUserStruct!T)
236 {
237     import std.conv: text;
238     import core.memory: GC;
239 
240     scope str = text(value);
241 
242     auto ret = str.toXlOper(allocator);
243     () @trusted { GC.free(cast(void*)str.ptr); }();
244 
245     return ret;
246 }
247 
248 XLOPER12 toXlOper(T, A)(T value, ref A allocator)
249     @trusted
250     if(is(T: Tuple!A, A...))
251 {
252     import std.experimental.allocator: makeArray;
253 
254     XLOPER12 oper;
255 
256     oper.xltype = XlType.xltypeMulti;
257     oper.val.array.rows = 1;
258     oper.val.array.columns = value.length;
259     oper.val.array.lparray = allocator.makeArray!XLOPER12(T.length).ptr;
260 
261     static foreach(i; 0 .. T.length) {
262         oper.val.array.lparray[i] = value[i].toXlOper(allocator);
263     }
264 
265     return oper;
266 }
267 
268 
269 /**
270   creates an XLOPER12 that can be returned to Excel which
271   will be freed by Excel itself
272  */
273 XLOPER12 toAutoFreeOper(T)(T value) {
274     import xlld.memorymanager: autoFreeAllocator;
275     import xlld.sdk.xlcall: XlType;
276 
277     auto result = value.toXlOper(autoFreeAllocator);
278     result.xltype |= XlType.xlbitDLLFree;
279     return result;
280 }