1 /** 2 Conversions from D types to XLOPER12 3 */ 4 module xlld.conv.to; 5 6 7 import xlld.from; 8 import xlld.conv.misc: isUserStruct, isVector, isSomeString, isTuple; 9 import xlld.sdk.xlcall: XLOPER12, XlType; 10 import xlld.any: Any; 11 import xlld.wrap.wrap: isWantedType; 12 import std.traits: isIntegral, Unqual; 13 import std.datetime: DateTime; 14 import std.range.primitives: isInputRange; 15 16 17 alias FromEnumConversionFunction = string delegate(int) @safe; 18 package __gshared FromEnumConversionFunction[string] gFromEnumConversions; 19 shared from!"core.sync.mutex".Mutex gFromEnumMutex; 20 21 22 /// 23 XLOPER12 toXlOper(T)(T val) { 24 import std.experimental.allocator: theAllocator; 25 return toXlOper(val, theAllocator); 26 } 27 28 29 /// 30 XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(isIntegral!T) { 31 import xlld.sdk.xlcall: XlType; 32 33 auto ret = XLOPER12(); 34 ret.xltype = XlType.xltypeInt; 35 ret.val.w = val; 36 37 return ret; 38 } 39 40 41 /// 42 XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(is(Unqual!T == double)) { 43 import xlld.sdk.xlcall: XlType; 44 45 auto ret = XLOPER12(); 46 ret.xltype = XlType.xltypeNum; 47 ret.val.num = val; 48 49 return ret; 50 } 51 52 53 /// 54 __gshared immutable toXlOperMemoryException = new Exception("Failed to allocate memory for string oper"); 55 56 57 /// 58 XLOPER12 toXlOper(T, A)(in T val, ref A allocator) 59 if(isSomeString!T) 60 { 61 import xlld.sdk.xlcall: XCHAR; 62 import std.utf: byWchar; 63 64 const numBytes = numOperStringBytes(val); 65 auto wval = () @trusted { return cast(wchar[]) allocator.allocate(numBytes); }(); 66 if(&wval[0] is null) 67 throw toXlOperMemoryException; 68 69 int i = 1; 70 71 static if(__traits(compiles, val.range)) 72 auto range = val.range; 73 else 74 alias range = val; 75 76 foreach(ch; range.byWchar) { 77 wval[i++] = ch; 78 } 79 80 wval[0] = cast(ushort)(i - 1); 81 82 auto ret = XLOPER12(); 83 ret.xltype = XlType.xltypeStr; 84 () @trusted { ret.val.str = cast(XCHAR*)&wval[0]; }(); 85 86 return ret; 87 } 88 89 90 91 /// the number of bytes required to store `str` as an XLOPER12 string 92 package size_t numOperStringBytes(T)(in T str) 93 if(isSomeString!T) 94 { 95 // XLOPER12 strings are wide strings where index 0 is the length 96 // and [1 .. $] is the actual string 97 return cast(typeof(return)) ((str.length + 1) * wchar.sizeof); 98 } 99 100 101 private template hasLength(R) { 102 enum hasLength = is(typeof( 103 { 104 import std.traits: isIntegral; 105 auto l = R.init.length; 106 static assert(isIntegral!(typeof(l))); 107 })); 108 } 109 110 /// If we can get the length of R and R is an input range 111 private template isLengthRange(R) { 112 import xlld.conv.misc: isVector; 113 import std.range.primitives: isInputRange, isForwardRange; 114 115 enum isLengthRange = 116 isForwardRange!R 117 || (isInputRange!R && hasLength!R) 118 // Vector is no longer a range itself, but we pretend it is 119 || (isVector!R) 120 ; 121 } 122 123 /// If it's a 1D range 124 private template isRange1D(T) { 125 import std.range.primitives: ElementType; 126 127 enum isRange1D = 128 isLengthRange!T 129 // the element type has to itself not be a range, which in our case 130 // means a "proper" scalar or a string of some sort 131 && (!isLengthRange!(ElementType!T) || isSomeString!(ElementType!T)) 132 && !isSomeString!T 133 ; 134 } 135 136 ///If it's a 2D range 137 private template isRange2D(T) { 138 import std.range.primitives: ElementType; 139 enum isRange2D = isLengthRange!T && isRange1D!(ElementType!T); 140 } 141 142 143 XLOPER12 toXlOper(T, A)(auto ref T vector, ref A allocator) 144 if(isVector!T && !(is(Unqual!(typeof(vector[0])) == char))) 145 { 146 return vector.range.toXlOper(allocator); 147 } 148 149 150 XLOPER12 toXlOper(T, A)(T range, ref A allocator) 151 if(isRange1D!T) 152 { 153 import std.range: only; 154 return only(range).toXlOper(allocator); 155 } 156 157 XLOPER12 toXlOper(T, A)(T range, ref A allocator) 158 if(isRange2D!T) 159 { 160 import xlld.conv.misc: multi; 161 import std.range: walkLength; 162 import std.array: save, front; 163 import std.algorithm: any; 164 import std.range.primitives: isForwardRange; 165 166 static __gshared immutable shapeException = new Exception("# of columns must all be the same and aren't"); 167 168 const rows = cast(int) range.rangeLength; 169 const frontLength = range.front.rangeLength; 170 171 static if(isForwardRange!T) 172 auto checkRange = range.save; 173 else 174 alias checkRange = range; 175 176 if(checkRange.any!(r => r.rangeLength != frontLength)) 177 throw shapeException; 178 179 const cols = cast(int) range.front.rangeLength; 180 auto ret = multi(rows, cols, allocator); 181 auto opers = () @trusted { return ret.val.array.lparray[0 .. rows*cols]; }(); 182 183 int i = 0; 184 foreach(ref subRange; range) { 185 foreach(ref elt; subRange) { 186 opers[i++] = elt.toXlOper(allocator); 187 } 188 } 189 190 return ret; 191 } 192 193 private auto rangeLength(R)(auto ref R range) 194 if(isInputRange!R || isVector!R) 195 { 196 import std.range.primitives: isForwardRange; 197 198 static if(hasLength!R) 199 return range.length; 200 else static if(isForwardRange!R) { 201 import std.range.primitives: walkLength; 202 import std.array: save; 203 return range.save.walkLength; 204 } else 205 static assert(false, "Can't get length for " ~ R.stringof); 206 } 207 208 209 /// 210 XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == Any)) { 211 import xlld.conv.misc: dup; 212 return value.dup(allocator); 213 } 214 215 216 XLOPER12 toXlOper(T, A)(T value, ref A allocator, in string file = __FILE__, in size_t line = __LINE__) 217 @safe if(is(Unqual!T == DateTime)) 218 { 219 import xlld.sdk.framework: Excel12f; 220 import xlld.sdk.xlcall: xlfDate, xlfTime, xlretSuccess; 221 222 XLOPER12 ret, date, time; 223 224 auto year = value.year.toXlOper(allocator); 225 auto month = () @trusted { return toXlOper(cast(int)value.month, allocator); }(); 226 auto day = value.day.toXlOper(allocator); 227 228 () @trusted { 229 assert(year.xltype == XlType.xltypeInt); 230 assert(month.xltype == XlType.xltypeInt); 231 assert(day.xltype == XlType.xltypeInt); 232 }(); 233 234 const dateCode = () @trusted { return Excel12f(xlfDate, &date, &year, &month, &day); }(); 235 if(dateCode != xlretSuccess) 236 throw new Exception("Error calling xlfDate", file, line); 237 () @trusted { assert(date.xltype == XlType.xltypeNum); }(); 238 239 auto hour = value.hour.toXlOper(allocator); 240 auto minute = value.minute.toXlOper(allocator); 241 auto second = value.second.toXlOper(allocator); 242 243 const timeCode = () @trusted { return Excel12f(xlfTime, &time, &hour, &minute, &second); }(); 244 if(timeCode != xlretSuccess) 245 throw new Exception("Error calling xlfTime", file, line); 246 247 () @trusted { assert(time.xltype == XlType.xltypeNum); }(); 248 249 ret.xltype = XlType.xltypeNum; 250 ret.val.num = date.val.num + time.val.num; 251 return ret; 252 } 253 254 255 XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == bool)) { 256 import xlld.sdk.xlcall: XlType; 257 XLOPER12 ret; 258 ret.xltype = XlType.xltypeBool; 259 ret.val.bool_ = cast(typeof(ret.val.bool_)) value; 260 return ret; 261 } 262 263 /** 264 Register a custom conversion from enum (going through integer) to a string. 265 This function will be called to convert enum return values from wrapped 266 D functions into strings in Excel. 267 */ 268 void registerConversionFrom(T)(FromEnumConversionFunction func) @trusted { 269 import std.traits: fullyQualifiedName; 270 271 assert(gFromEnumMutex !is null, "gFromEnumMutex is null"); 272 273 gFromEnumMutex.lock_nothrow; 274 scope(exit)gFromEnumMutex.unlock_nothrow; 275 276 gFromEnumConversions[fullyQualifiedName!T] = func; 277 } 278 279 280 void unregisterConversionFrom(T)() @trusted { 281 import std.traits: fullyQualifiedName; 282 283 assert(gFromEnumMutex !is null, "gFromEnumMutex is null"); 284 285 gFromEnumMutex.lock_nothrow; 286 scope(exit)gFromEnumMutex.unlock_nothrow; 287 288 gFromEnumConversions.remove(fullyQualifiedName!T); 289 } 290 291 292 293 294 XLOPER12 toXlOper(T, A)(T value, ref A allocator) @trusted if(is(T == enum)) { 295 296 import std.conv: text; 297 import std.traits: fullyQualifiedName; 298 import core.memory: GC; 299 300 enum name = fullyQualifiedName!T; 301 302 { 303 assert(gFromEnumMutex !is null, "gFromEnumMutex is null"); 304 305 gFromEnumMutex.lock_nothrow; 306 scope(exit) gFromEnumMutex.unlock_nothrow; 307 308 if(name in gFromEnumConversions) 309 return gFromEnumConversions[name](value).toXlOper(allocator); 310 } 311 312 scope str = text(value); 313 auto ret = str.toXlOper(allocator); 314 () @trusted { GC.free(cast(void*)str.ptr); }(); 315 316 return ret; 317 } 318 319 XLOPER12 toXlOper(T, A)(T value, ref A allocator) 320 if(isUserStruct!T) 321 { 322 import std.conv: text; 323 import core.memory: GC; 324 325 scope str = text(value); 326 327 auto ret = str.toXlOper(allocator); 328 () @trusted { GC.free(cast(void*)str.ptr); }(); 329 330 return ret; 331 } 332 333 XLOPER12 toXlOper(T, A)(T value, ref A allocator) 334 if(isTuple!T) 335 { 336 import xlld.conv.misc: multi; 337 import std.experimental.allocator: makeArray; 338 339 const rows = 1; 340 const cols = value.length; 341 342 auto ret = multi(rows, cols, allocator); 343 auto opers = () @trusted { return ret.val.array.lparray[0 .. rows*cols]; }(); 344 345 static foreach(i; 0 .. value.length) { 346 opers[i] = value[i].toXlOper(allocator); 347 } 348 349 return ret; 350 } 351 352 353 XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == XLOPER12)) 354 { 355 return value; 356 } 357 358 359 /** 360 creates an XLOPER12 that can be returned to Excel which 361 will be freed by Excel itself 362 */ 363 XLOPER12 toAutoFreeOper(T)(T value) { 364 import xlld.memorymanager: autoFreeAllocator; 365 import xlld.sdk.xlcall: XlType; 366 367 auto result = value.toXlOper(autoFreeAllocator); 368 result.xltype |= XlType.xlbitDLLFree; 369 return result; 370 }