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 }