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";