1 /** 2 Interface for registering worksheet functions with Excel 3 */ 4 module xlld.wrap.worksheet; 5 6 /** 7 Simple wrapper struct for a value. Provides a type-safe way 8 of making sure positional arguments match the intended semantics, 9 which is important given that nearly all of the arguments for 10 worksheet function registration are of the same type: wstring 11 ST is short for "SmallType". 12 */ 13 private mixin template ST(string name, T = wstring) { 14 mixin(`struct ` ~ name ~ `{ T value; }`); 15 } 16 17 // see: https://docs.microsoft.com/en-us/office/client-developer/excel/xlfregister-form-1 18 19 /// 20 struct Procedure { 21 wstring value; 22 string toString() @safe pure const { 23 import std.conv: to; 24 return value.to!string; 25 } 26 } 27 28 mixin ST!"TypeText"; 29 /++ 30 Function name as it will appear in the Function Wizard. 31 +/ 32 mixin ST!"FunctionText"; 33 /++ 34 Names of the arguments, semicolon separated. For example: 35 36 foo;bar 37 +/ 38 mixin ST!"ArgumentText"; 39 mixin ST!"MacroType"; 40 /++ 41 Category name it appears in in the wizard. Must be picked 42 from the list of existing categories in Excel. 43 +/ 44 mixin ST!"Category"; 45 /++ 46 One-character, case-sensitive key. "A" assigns this command 47 to ctrl+shift+A. Used only for commands. 48 +/ 49 mixin ST!"ShortcutText"; 50 /++ 51 Reference to the help file to display when the user clicks help. 52 Form of `filepath!HelpContextID` or `URL!0`. 53 54 The !0 is required if you provide a web link. 55 +/ 56 mixin ST!"HelpTopic"; 57 /++ 58 Describes your function in the Function Wizard. 59 ++/ 60 mixin ST!"FunctionHelp"; 61 /++ 62 Array of text strings displayed in the function dialog 63 in Excel to describe each arg. 64 +/ 65 struct ArgumentHelp { 66 wstring[] value; 67 68 // allow @ArgumentHelp(x, y, x) too 69 this(wstring[] txt...) pure nothrow @safe { 70 // this is fine because below it is all copied 71 // into GC memory anyway. 72 this(txt[]); 73 } 74 75 this(scope wstring[] txt) pure nothrow @safe { 76 foreach(t; txt) add(t); 77 } 78 79 void add(wstring txt) @safe pure nothrow { 80 // Excel has a bug that chops off the last 81 // character of this, so adding a space here 82 // works around that. 83 value ~= txt ~ " "; 84 } 85 } 86 87 88 /** 89 The arguments used to register a worksheet function with the spreadsheet. 90 */ 91 struct WorksheetFunction { 92 // the first few parameters have to be set, the others are optional 93 Procedure procedure; 94 TypeText typeText; 95 FunctionText functionText; 96 Optional optional; 97 alias optional this; //for ease of use 98 99 /** 100 Returns an array suitable for use with spreadsheet registration 101 */ 102 const(wstring)[] toStringArray() @safe pure const nothrow { 103 return 104 [ 105 procedure.value, typeText.value, 106 functionText.value, argumentText.value, 107 macroType.value, category.value, 108 shortcutText.value, helpTopic.value, functionHelp.value 109 ] ~ argumentHelp.value; 110 } 111 112 bool opEquals(const WorksheetFunction rhs) const @safe pure { return optional == rhs.optional; } 113 } 114 115 // helper template to type-check variadic template constructor below 116 private alias toType(alias U) = typeof(U); 117 118 /** 119 Optional arguments that can be set by a function author, but don't necessarily 120 have to be. 121 */ 122 struct Optional { 123 ArgumentText argumentText; 124 MacroType macroType = MacroType("1"w); 125 Category category; 126 ShortcutText shortcutText; 127 HelpTopic helpTopic; 128 FunctionHelp functionHelp; 129 ArgumentHelp argumentHelp; 130 131 this(T...)(T args) { 132 import std.meta: staticIndexOf, staticMap, allSatisfy, AliasSeq; 133 import std.conv: text; 134 135 static assert(T.length <= this.tupleof.length, "Too many arguments for Optional/Register"); 136 137 // myTypes: ArgumentText, MacroType, ... 138 alias myTypes = staticMap!(toType, AliasSeq!(this.tupleof)); 139 enum isOneOfMyTypes(U) = staticIndexOf!(U, myTypes) != -1; 140 static assert(allSatisfy!(isOneOfMyTypes, T), 141 text("Unknown types passed to Optional/Register constructor. ", 142 "Has to be one of:\n", myTypes.stringof)); 143 144 // loop over whatever was given and set each of our members based on the 145 // type of the parameter instead of by position 146 foreach(ref member; this.tupleof) { 147 enum index = staticIndexOf!(typeof(member), T); 148 static if(index != -1) 149 member = args[index]; 150 } 151 } 152 } 153 154 /** 155 A user-facing name to use as an UDA to decorate D functions. 156 Any arguments passed to its constructor will be used to register 157 the function with the spreadsheet. 158 */ 159 alias Register = Optional; 160 alias Excel = Optional; 161 162 163 /// 164 struct Dispose(alias function_) { 165 alias dispose = function_; 166 } 167 168 /// Information about a function parameter. Meant to be used as a UDA. 169 mixin ST!"ExcelParameter";