1 module janet.wrap;
2 
3 import janet.c;
4 
5 import janet : JanetStrType, JanetAbstractClassHelper;
6 
7 import std.meta : AliasSeq;
8 
9 import std.algorithm.searching : startsWith;
10 private enum isInternal(string field) = field.startsWith("__");
11 
12 import std.traits : isSomeString,isArray,isAssociativeArray,isSomeFunction,isPointer,ReturnType, Parameters;
13 
14 import std.traits : functionAttributes, SetFunctionAttributes,isFunction;
15 
16 template IsJanetCFunc(T)
17 {
18     static if(isSomeFunction!T)
19     {
20         alias strippedFuncType = SetFunctionAttributes!(T,"C",functionAttributes!JanetCFunction);
21         enum IsJanetCFunc = is(strippedFuncType == JanetCFunction);
22     }
23     else
24     {
25         enum IsJanetCFunc = false;
26     }
27 }
28 
29 /// Converts a Janet string to a D string.
30 @nogc string fromJanetString(Janet janet)
31 {
32     import std.string : fromStringz;
33     const auto unwrapped = janet_unwrap_string(janet);
34     const(char)* charArray = cast(const(char)*)(unwrapped);
35     return cast(string)fromStringz(charArray);
36 }
37 /// ditto
38 @nogc string fromJanetString(Janet* janet)
39 {
40     import std.string : fromStringz;
41     const auto unwrapped = janet_unwrap_string(*janet);
42     const(char)* charArray = cast(const(char)*)(unwrapped);
43     return cast(string)fromStringz(charArray);
44 }
45 /// Returns true if the Janet argument's type is compatible with the template type. Used for type checking at runtime.
46 pure @safe @nogc bool janetCompatible(T)(Janet janet)
47 {
48     static if(is(T == bool))
49     {
50         return janet.type == JanetType.JANET_BOOLEAN;
51     }
52     else static if(is(T : double) || is(T : int))
53     {
54         return janet.type == JanetType.JANET_NUMBER;
55     }
56     else static if(is(T == JanetFiber*))
57     {
58         return janet.type == JanetType.JANET_FIBER;
59     }
60     else static if(is(T == JanetFunction*))
61     {
62         return janet.type == JanetType.JANET_FUNCTION;
63     }
64     else static if(isSomeString!T)
65     {
66         return janet.type == JanetType.JANET_STRING || janet.type == JanetType.JANET_SYMBOL || janet.type == JanetType.JANET_KEYWORD || janet.type == JanetType.JANET_BUFFER;
67     }
68     else static if(isArray!T)
69     {
70         return janet.type == JanetType.JANET_ARRAY || janet.type == JanetType.JANET_TUPLE;
71     }
72     else static if(isAssociativeArray!T || is(T == struct))
73     {
74         return janet.type == JanetType.JANET_TABLE || janet.type == JanetType.JANET_STRUCT;
75     }
76     else static if(IsJanetCFunc!T)
77     {
78         return janet.type == JanetType.JANET_CFUNCTION;
79     }
80     else static if(is(T==class))
81     {
82         return janet.type == JanetType.JANET_ABSTRACT;
83     }
84     else static if(isPointer!T)
85     {
86         return janet.type == JanetType.JANET_POINTER;
87     }
88     else
89     {
90         return false;
91     }
92 }
93 
94 pure @safe @nogc bool janetCompatible(T)(T x,Janet janet)
95 {
96     return janetCompatible!(T)(janet);
97 }
98 
99 @nogc T as(T)(Janet janet)
100     if(canBeSafelyConverted!T || isSomeString!T || is(T == struct) || isPointer!T)
101 {
102     return (&janet).as!T;
103 }
104 
105 T as(T)(Janet janet)
106     if(is(T == class))
107 {
108     return (&janet).as!T;
109 }
110 
111 private bool canBeSafelyConverted(T)()
112 {
113     return !(isSomeString!T || is(T == class) || is(T == struct) || isPointer!T);
114 }
115 
116 /** Converts from a Janet object to an object of the given type
117     A safe version of the function will be used for all types that can be safely converted.*/
118 @safe @nogc T as(T)(Janet* janet)
119     if(canBeSafelyConverted!T)
120 {
121     static if(is(T == bool))
122     {
123         return cast(bool)janet_getboolean(janet,0);
124     }
125     else static if(is(T : int))
126     {
127         return janet_getinteger(janet,0);
128     }
129     else static if(is(T : double))
130     {
131         return janet_getnumber(janet,0);
132     }
133     else static if(is(T == JanetFiber*))
134     {
135         return janet_getfiber(janet,0);
136     }
137     else static if(is(T == JanetFunction*))
138     {
139         return janet_getfunction(janet,0);
140     }
141     else static if(is(T == JanetArray*))
142     {
143         return janet_getarray(janet,0);
144     }
145     else static if(is(T == JanetTable*))
146     {
147         return janet_gettable(janet,0);
148     }
149     else static if(is(T == JanetBuffer*))
150     {
151         return janet_getbuffer(janet,0);
152     }
153     else static if(IsJanetCFunc!T)
154     {
155         return janet_getcfunction(janet,0);
156     }
157 }
158 /// ditto
159 @nogc T as(T,JanetStrType strType = JanetStrType.STRING)(Janet* janet)
160     if(isPointer!T || isSomeString!T || is(T == struct))
161 {
162     static if(isPointer!T)
163     {
164         return cast(T)(janet_getpointer(janet,0));
165     }
166     else static if(isSomeString!T)
167     {
168         final switch(strType)
169         {
170             import std.traits : EnumMembers;
171             static foreach(t;EnumMembers!JanetStrType)
172             {
173                 case t:
174                     if(!janet_checktype(janet,mixin("JanetType.JANET_"~__traits(identifier,EnumMembers!JanetStrType[t]))))
175                     {
176                         janet_panic_type(*janet,0,mixin("JANET_TFLAG_"~__traits(identifier,EnumMembers!JanetStrType[t])));
177                     }
178                     return cast(T)(fromJanetString(janet));
179             }
180         }
181     }
182     else static if(is(T == struct))
183     {
184         JanetTable* tbl = janet_gettable(janet,0);
185         T newStruct = T.init;
186         for(int i=0;i<tbl.count;i++)
187         {
188             JanetKV kv = tbl.data[i];
189             static foreach(field; __traits(allMembers,T))
190             {
191                 static if(!isInternal!field &&
192                         field != "this" &&
193                         field != "opAssign" &&
194                         !isFunction!(mixin("T."~field))
195                         )
196                 {
197                     if(kv.key.as!string == field)
198                     {
199                         mixin("newStruct."~field) = as!(typeof(mixin("T."~field)))(kv.value);
200                     }
201                 }
202             }
203         }
204         return newStruct;
205     }
206 }
207 /// ditto
208 T as(T,JanetStrType strType = JanetStrType.STRING)(Janet* janet)
209     if(is(T==class))
210 {
211     // this assumes that wrap has previously been used to make the abstract.
212     // abstract objects initialized in janet must be gotten in a different way.
213     // i'll probably figure it out eventually, really.
214     // and i know that this is a terrible thing to read in comments.
215     import janet.register : registerType;
216     return cast(T)(*(cast(void**)(janet_getabstract(janet,0,registerType!T))));
217 }
218 
219 unittest
220 {
221     int r = 0;
222     int* pr = &r;
223     Janet j;
224     j = janetWrap(pr);
225     pr = (j).as!(int*);
226 }
227 
228 
229 /**
230     Wraps a D value to a Janet value.
231 */
232 @safe @nogc Janet janetWrap()
233 {
234     return janet_wrap_nil();
235 }
236 /// ditto
237 @nogc Janet janetWrap(T,JanetStrType strType = JanetStrType.STRING)(T x)
238 {
239     /*For the record, if I try to do operator overloading with this, I get this wonderful error:
240         source\d\function.d(40,43): Error: none of the overloads of janetWrap are callable using argument types (int), candidates are:
241         source\d\wrap.d(193,19):        janet.wrap.janetWrap(int x)
242     so if you're wondering why this is an else-static-if template even though it has one return type, that's why.
243     */
244     static if(is(T==Janet))
245     {
246         return x;
247     }
248     else static if(is(T == bool))
249     {
250         return janet_wrap_boolean(x);
251     }
252     else static if(is(T==void*))
253     {
254         return janet_wrap_abstract(x);
255     }
256     else static if(IsJanetCFunc!T)
257     {
258         return janet_wrap_cfunction(x);
259     }
260     else static if(isSomeString!T)
261     {
262         final switch(strType)
263         {
264             case JanetStrType.STRING:
265                 return janet_wrap_string(janet_string(cast(ubyte*)x,cast(int)x.length));
266             case JanetStrType.SYMBOL:
267                 return janet_wrap_symbol(janet_string(cast(ubyte*)x,cast(int)x.length));
268             case JanetStrType.KEYWORD:
269                 return janet_wrap_keyword(janet_string(cast(ubyte*)x,cast(int)x.length));
270         }
271     }
272     else static if(is(T==const(char)*))
273     {
274         final switch(strType)
275         {
276             case JanetStrType.STRING:
277                 return janet_wrap_string(janet_cstring(x));
278             case JanetStrType.SYMBOL:
279                 return janet_wrap_symbol(janet_cstring(x));
280             case JanetStrType.KEYWORD:
281                 return janet_wrap_keyword(janet_cstring(x));
282         }
283     }
284     else static if(isPointer!T)
285     {
286         return janet_wrap_pointer(x);
287     }
288     else static if(is(T : double))
289     {
290         return janet_wrap_number_safe(x);
291     }
292     else static if(is(T : int))
293     {
294         return janet_wrap_integer(x);
295     }
296     else static if(isArray!T)
297     {
298         JanetArray* arr = janet_array(cast(int)x.length);
299         foreach(item; x)
300         {
301             janet_array_push(arr,janetWrap(item));
302         }
303         return janet_wrap_array(arr);
304     }
305     else static if(is(T == struct))
306     {
307         JanetTable* tbl = janet_table(x.tupleof.length);
308         static foreach(field; __traits(allMembers,T))
309         {
310             static if(!isInternal!field &&
311                     field != "this" &&
312                     field != "opAssign") // also from LuaD
313             {
314                 static if(isFunction!(mixin("x."~field)))
315                 {
316                     import janet.func : makeJanetCFunc;
317                     janet_table_put(tbl,janetWrap(field),janetWrap(makeJanetCFunc!(mixin("T."~field))(&x)));
318                 }
319                 else
320                 {
321                     janet_table_put(tbl,janetWrap(field),mixin("janetWrap(x."~field~")"));
322                 }
323             }
324         }
325         return janet_wrap_table(tbl);
326     }
327     else static if(is(T == class) || is(T == struct))
328     {
329         import janet.register : registerType;
330         auto data = cast(JanetAbstractClassHelper!T*)janet_abstract(registerType!T,JanetAbstractClassHelper!T.sizeof);
331         data.obj = x;
332         return janet_wrap_abstract(data);
333     }
334     else
335     {
336         static assert(0,"Not a compatible type for janet wrap!");
337     }
338 }
339 
340 unittest
341 {
342     auto J=Janet();
343     J=janetWrap(J);
344     J=janetWrap(false);
345     J=janetWrap(cast(void*)&J);
346     J=janetWrap("Wrapping a string!");
347     J=janetWrap(&J);
348     J=janetWrap(5.0);
349     J=janetWrap(4);
350     J=janetWrap([1,2,3,4,5]);
351     struct SmallStruct
352     {
353         int a=4;
354         int b=5;
355         float c=4.4;
356     }
357     SmallStruct st;
358     J=janetWrap(st);
359 }
360 
361 /// ditto
362 @nogc Janet janetWrap(alias func)()
363 {
364     import janet.func : makeJanetCFunc;
365     return janetWrap(makeJanetCFunc!func);
366 }
367 
368 /// ditto
369 @nogc Janet janetWrap(K,V)(V[K] arr)
370 {
371     JanetTable* tbl = janet_table(arr.length);
372     foreach(K key,V value; arr)
373     {
374         janet_table_put(arr,janetWrap(key),wrjanetWrapap(value));
375     }
376     return janet_wrap_table(arr);
377 }
378 
379 version(unittest)
380 {
381     pure @safe @nogc int baz(int a)
382     {
383         return a+1;
384     }
385 }
386 
387 unittest
388 {
389     auto fun = janetWrap!baz;
390 
391 }
392 unittest
393 {
394     import std.stdio : writeln;
395     import janet : coreEnv;
396     writeln("Starting wrap test.");
397     const string foo = "foo";
398     Janet* j;
399     auto janetFoo = janetWrap(foo);
400     import std.string : toStringz;
401     janet_def(coreEnv,toStringz("foo"),janetFoo,"A simple string, which should say 'foo'.");
402     const auto janetedString = as!string(janetFoo);
403     assert(janetedString == foo,janetedString~" is not "~foo~". This is likely caused by compiling with wrong settings (turn nanboxing off!)");
404 }