Skip to content

Commit

Permalink
Fix #24: Autowrap std.datetime.Datetime
Browse files Browse the repository at this point in the history
  • Loading branch information
atilaneves committed Oct 26, 2017
2 parents d27ad22 + d129d7f commit 97bb0d4
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 30 deletions.
7 changes: 7 additions & 0 deletions example/d_funcs.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import xlld;
import std.datetime: DateTime;

@Register(ArgumentText("Array to add"),
HelpTopic("Adds all cells in an array"),
Expand Down Expand Up @@ -145,3 +146,9 @@ string[][] FirstOfTwoAnyArraysToString(Any[][] testarg, Any[][] rhs) nothrow {
return [[e.msg]];
}
}

string DateTimeToString(DateTime dt) @safe {
import std.conv: text;
return text("year: ", dt.year, ", month: ", dt.month, ", day: ", dt.day,
", hour: ", dt.hour, ", minute: ", dt.minute, ", second: ", dt.second);
}
9 changes: 5 additions & 4 deletions source/xlld/framework.d
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,15 @@ T excel12(T, A...)(int xlfn, auto ref A args) @trusted {
import xlld.memorymanager: gTempAllocator, autoFreeAllocator;
import xlld.wrap: toXlOper, fromXlOper;
import xlld.xlcall: xlretSuccess;
import std.experimental.allocator.gc_allocator: GCAllocator;

alias allocator = GCAllocator.instance;

XLOPER12[A.length] operArgs;
LPXLOPER12[A.length] operArgPtrs;

scope(exit) gTempAllocator.deallocateAll;

foreach(i, _; A) {
operArgs[i] = args[i].toXlOper(gTempAllocator);
operArgs[i] = args[i].toXlOper(allocator);
operArgPtrs[i] = &operArgs[i];
}

Expand Down Expand Up @@ -187,7 +188,7 @@ auto excel12Then(alias F, A...)(int xlfn, auto ref A args) {
alias RealType = Unqual!T;

void freeRet(U)() @trusted {
autoFreeAllocator.dispose(cast()excelRet);
autoFreeAllocator.dispose(cast(U)excelRet);
}

static if(__traits(compiles, freeRet!RealType()))
Expand Down
5 changes: 5 additions & 0 deletions source/xlld/test_d_funcs.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module xlld.test_d_funcs;
version(unittest):

import xlld;
import std.datetime: DateTime;


///
Expand Down Expand Up @@ -239,3 +240,7 @@ int Twice(int i) @safe nothrow {
double FuncConstDouble(const double a) @safe nothrow {
return a;
}

double DateTimeToDouble(DateTime d) @safe nothrow {
return d.year * 2;
}
27 changes: 21 additions & 6 deletions source/xlld/test_util.d
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ void*[maxCoerce] gCoerced;
///
void*[maxCoerce] gFreed;
///
double[] gDates;
///
double[] gTimes;
double[] gDates, gTimes, gYears, gMonths, gDays, gHours, gMinutes, gSeconds;

///
static this() {
Expand Down Expand Up @@ -101,12 +99,28 @@ extern(Windows) int excel12UnitTest(int xlfn, int numOpers, LPXLOPER12 *opers, L
return xlretSuccess;

case xlfDate:

return returnGlobalMockFrom(gDates, result);

case xlfTime:

return returnGlobalMockFrom(gTimes, result);

case xlfYear:
return returnGlobalMockFrom(gYears, result);

case xlfMonth:
return returnGlobalMockFrom(gMonths, result);

case xlfDay:
return returnGlobalMockFrom(gDays, result);

case xlfHour:
return returnGlobalMockFrom(gHours, result);

case xlfMinute:
return returnGlobalMockFrom(gMinutes, result);

case xlfSecond:
return returnGlobalMockFrom(gSeconds, result);
}
}

Expand All @@ -115,8 +129,9 @@ private int returnGlobalMockFrom(R)(R values, LPXLOPER12 result) if(isInputRange
import xlld.xlcall: xlretSuccess;
import std.array: front, popFront, empty;
import std.experimental.allocator.mallocator: Mallocator;
import std.range: ElementType;

const ret = values.empty ? 0.0 : values.front;
const ret = values.empty ? ElementType!R.init : values.front;
if(!values.empty) values.popFront;

*result = ret.toXlOper(Mallocator.instance);
Expand Down
150 changes: 132 additions & 18 deletions source/xlld/wrap.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,38 @@ import xlld.memorymanager: autoFree;
import xlld.framework: freeXLOper;
import xlld.worksheet;
import xlld.any: Any;
import std.traits: Unqual;

import std.traits: Unqual, isIntegral;
import std.datetime: DateTime;


version(unittest) {
import unit_threaded;
import xlld.test_util: TestAllocator, shouldEqualDlang, toSRef;
import xlld.test_util: TestAllocator, shouldEqualDlang, toSRef, gDates, gTimes,
gYears, gMonths, gDays, gHours, gMinutes, gSeconds;
import std.experimental.allocator.mallocator: Mallocator;
import std.experimental.allocator.gc_allocator: GCAllocator;
import xlld.any: any;
alias theMallocator = Mallocator.instance;
alias theGC = GCAllocator.instance;
}


///
XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(is(Unqual!T == int)) {
XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(isIntegral!T) {
auto ret = XLOPER12();
ret.xltype = XlType.xltypeInt;
ret.val.w = val;
return ret;
}

///
@("toExcelOper!int")
unittest {
auto oper = 42.toXlOper(theMallocator);
oper.xltype.shouldEqual(XlType.xltypeInt);
oper.val.w.shouldEqual(42);
}


///
XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(is(Unqual!T == double)) {
Expand All @@ -41,9 +51,16 @@ XLOPER12 toXlOper(T, A)(in T val, ref A allocator) if(is(Unqual!T == double)) {
}

///
__gshared immutable toXlOperMemoryException = new Exception("Failed to allocate memory for string oper");
@("toExcelOper!double")
unittest {
auto oper = (42.0).toXlOper(theMallocator);
oper.xltype.shouldEqual(XlType.xltypeNum);
oper.val.num.shouldEqual(42.0);
}

///
__gshared immutable toXlOperShapeException = new Exception("# of columns must all be the same and aren't");
__gshared immutable toXlOperMemoryException = new Exception("Failed to allocate memory for string oper");


///
XLOPER12 toXlOper(T, A)(in T val, ref A allocator)
Expand Down Expand Up @@ -136,6 +153,9 @@ package size_t numOperStringBytes(ref const(XLOPER12) oper) @trusted @nogc pure
return (oper.val.str[0] + 1) * wchar.sizeof;
}

///
__gshared immutable toXlOperShapeException = new Exception("# of columns must all be the same and aren't");


///
XLOPER12 toXlOper(T, A)(T[][] values, ref A allocator)
Expand Down Expand Up @@ -341,20 +361,51 @@ unittest {
autoFree(&oper); // normally this is done by Excel
}

///
XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == int)) {
XLOPER12 ret;
ret.xltype = XlType.xltypeInt;
ret.val.w = value;


XLOPER12 toXlOper(T, A)(T value, ref A allocator) if(is(Unqual!T == DateTime)) {
import xlld.framework: Excel12f;
XLOPER12 ret, date, time;

auto year = value.year.toXlOper(allocator);
auto month = value.month.toXlOper(allocator);
auto day = value.day.toXlOper(allocator);

const dateCode = () @trusted { return Excel12f(xlfDate, &date, &year, &month, &day); }();
assert(dateCode == xlretSuccess);
assert(date.xltype == XlType.xltypeNum);

auto hour = value.hour.toXlOper(allocator);
auto minute = value.minute.toXlOper(allocator);
auto second = value.second.toXlOper(allocator);

const timeCode = () @trusted { return Excel12f(xlfTime, &time, &hour, &minute, &second); }();
assert(timeCode == xlretSuccess);
assert(time.xltype == XlType.xltypeNum);

ret.xltype = XlType.xltypeNum;
ret.val.num = date.val.num + time.val.num;
return ret;
}

///
@("toExcelOper!int")
unittest {
auto oper = 42.toXlOper(theMallocator);
oper.xltype.shouldEqual(XlType.xltypeInt);
oper.val.w.shouldEqual(42);
@("toExcelOper!DateTime")
@safe unittest {
gDates = [42.0];
gTimes = [3.0];

const dateTime = DateTime(2017, 12, 31, 1, 2, 3);
auto oper = dateTime.toXlOper(theGC);

oper.xltype.shouldEqual(XlType.xltypeNum);
oper.val.num.shouldEqual(45.0);

gDates = [33.0];
gTimes = [4.0];

oper = dateTime.toXlOper(theGC);

oper.xltype.shouldEqual(XlType.xltypeNum);
oper.val.num.shouldEqual(37.0);
}

///
Expand Down Expand Up @@ -817,13 +868,56 @@ T fromXlOper(T, A)(LPXLOPER12 oper, ref A allocator) if(is(Unqual!T == Any[][]))
}
}

///
T fromXlOper(T, A)(LPXLOPER12 oper, ref A allocator) if(is(Unqual!T == DateTime)) {
import xlld.framework: Excel12f;
import xlld.xlcall: XlType, xlretSuccess, xlfYear, xlfMonth, xlfDay, xlfHour, xlfMinute, xlfSecond;

XLOPER12 ret;

auto get(int fn) @trusted {
const code = Excel12f(fn, &ret, oper);
assert(code == xlretSuccess);
// for some reason the Excel API returns doubles
assert(ret.xltype == XlType.xltypeNum);

return cast(int)ret.val.num;
}

return T(get(xlfYear), get(xlfMonth), get(xlfDay),
get(xlfHour), get(xlfMinute), get(xlfSecond));
}

///
@("fromXlOper!DateTime")
@system unittest {
XLOPER12 oper;

gYears = [2017];
gMonths = [12];
gDays = [31];
gHours = [1];
gMinutes = [2];
gSeconds = [3];

const dateTime = oper.fromXlOper!DateTime(theGC);

dateTime.year.shouldEqual(2017);
dateTime.month.shouldEqual(12);
dateTime.day.shouldEqual(31);
dateTime.hour.shouldEqual(1);
dateTime.minute.shouldEqual(2);
dateTime.second.shouldEqual(3);
}

private enum isWorksheetFunction(alias F) =
isSupportedFunction!(F,
int,
double, double[], double[][],
string, string[], string[][],
Any, Any[], Any[][],
int);
DateTime,
);

@safe pure unittest {
import xlld.test_d_funcs;
Expand All @@ -834,6 +928,7 @@ private enum isWorksheetFunction(alias F) =
static assert(isWorksheetFunction!FuncThrows);
static assert(isWorksheetFunction!DoubleArrayToAnyArray);
static assert(isWorksheetFunction!Twice);
static assert(isWorksheetFunction!DateTimeToDouble);
}

/**
Expand Down Expand Up @@ -1041,6 +1136,25 @@ string wrapModuleWorksheetFunctionsString(string moduleName)(string callingModul
FuncAsserts(&arg); // should not actually throw
}

///
@("Wrap a function that accepts a DateTime")
@system unittest {
mixin(wrapModuleWorksheetFunctionsString!"xlld.test_d_funcs");

const dateTime = DateTime(2017, 12, 31, 1, 2, 3);
gYears = [dateTime.year];
gMonths = [dateTime.month];
gDays = [dateTime.day];
gHours = [dateTime.hour];
gMinutes = [dateTime.minute];
gSeconds = [dateTime.second];

auto arg = dateTime.toXlOper(theGC);
const ret = DateTimeToDouble(&arg);

ret.xltype.stripMemoryBitmask.shouldEqual(XlType.xltypeNum);
ret.val.num.shouldEqual(2017 * 2);
}

private enum invalidXlOperType = 0xdeadbeef;

Expand Down
5 changes: 3 additions & 2 deletions source/xlld/xlf.d
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ int second(double date) @safe @nogc nothrow {
}

private int datePart(double date, int xlfn) @safe @nogc nothrow {
// the Excel APIs for some reason return a double
try
return cast(int)excel12!double(xlfn, date);
catch(Exception ex)
Expand All @@ -54,15 +55,15 @@ private int datePart(double date, int xlfn) @safe @nogc nothrow {
double date(int year, int month, int day) @safe @nogc nothrow {
import xlld.xlcall: xlfDate;
try
return cast(int)excel12!double(xlfDate, year, month, day);
return excel12!double(xlfDate, year, month, day);
catch(Exception ex)
return 0;
}

double time(int year, int month, int day) @safe @nogc nothrow {
import xlld.xlcall: xlfTime;
try
return cast(int)excel12!double(xlfTime, year, month, day);
return excel12!double(xlfTime, year, month, day);
catch(Exception ex)
return 0;
}
Expand Down

0 comments on commit 97bb0d4

Please sign in to comment.