1 module janet.wrap;
2 
3 import janet.c;
4 
5 import janet.d : JanetDAbstractHead;
6 
7 private template isInternal(string field) // grabbed up from LuaD
8 {
9 	enum isInternal = field.length >= 2 && field[0..2] == "__";
10 }
11 
12 import std.traits : isSomeString,isArray,isAssociativeArray,isPointer;
13 /// Converts a Janet string to a D string.
14 string fromJanetString(Janet janet)
15 {
16     import std.string : fromStringz;
17     const auto unwrapped = janet_unwrap_string(janet);
18     const(char)* charArray = cast(const(char)*)(unwrapped);
19     return cast(string)fromStringz(charArray);
20 }
21 /// Returns true if the Janet argument's type is compatible with the template type. Used for type checking at runtime.
22 bool janetCompatible(T)(Janet janet)
23 {
24     static if(is(T == bool))
25     {
26         return janet.type == JanetType.JANET_BOOLEAN;
27     }
28     else static if(is(T : double) || is(T : int))
29     {
30         return janet.type == JanetType.JANET_NUMBER;
31     }
32     else static if(is(T == JanetFiber*))
33     {
34         return janet.type == JanetType.JANET_FIBER;
35     }
36     else static if(is(T == JanetFunction*))
37     {
38         return janet.type == JanetType.JANET_FUNCTION;
39     }
40     else static if(isSomeString!T)
41     {
42         return janet.type == JanetType.JANET_STRING || janet.type == JanetType.JANET_SYMBOL || janet.type == JanetType.JANET_KEYWORD || janet.type == JanetType.JANET_BUFFER;
43     }
44     else static if(isArray!T)
45     {
46         return janet.type == JanetType.JANET_ARRAY || janet.type == JanetType.JANET_TUPLE;
47     }
48     else static if(isAssociativeArray!T || is(T == struct))
49     {
50         return janet.type == JanetType.JANET_TABLE || janet.type == JanetType.JANET_STRUCT;
51     }
52     else static if(is(T==JanetCFunction))
53     {
54         return janet.type == JanetType.JANET_CFUNCTION;
55     }
56     else static if(is(T==class))
57     {
58         return janet.type == JanetType.JANET_ABSTRACT;
59     }
60     else static if(isPointer!T)
61     {
62         return janet.type == JanetType.JANET_POINTER;
63     }
64     else
65     {
66         return false;
67     }
68 }
69 
70 bool janetCompatible(T)(T x,Janet janet)
71 {
72     return janetCompatible!(T)(janet);
73 }
74 
75 /** Converts from a Janet object to an object of the given type.
76     Does absolutely no checking on its own! You must check or ensure that it will never coerce to an invalid type yourself.*/
77 @system T getFromJanet(T)(Janet janet)
78 {
79     static if(is(T == bool))
80     {
81         return cast(bool)janet_unwrap_boolean(janet);
82     }
83     else static if(is(T : int))
84     {
85         return janet_unwrap_integer(janet);
86     }
87     else static if(is(T : double))
88     {
89         return janet_unwrap_number(janet);
90     }
91     else static if(is(T == JanetFiber*))
92     {
93         return janet_unwrap_fiber(janet);
94     }
95     else static if(is(T == JanetFunction*))
96     {
97         return janet_unwrap_function(janet);
98     }
99     else static if(isSomeString!T)
100     {
101         return cast(T)(fromJanetString(janet));
102     }
103     else static if(is(T == JanetArray*))
104     {
105         return janet_unwrap_array(janet);
106     }
107     else static if(is(T == JanetTable*))
108     {
109         return janet_unwrap_table(janet);
110     }
111     else static if(is(T == JanetBuffer*))
112     {
113         return janet_unwrap_buffer(janet);
114     }
115     else static if(is(T == class))
116     {
117         return cast(T)(janet_unwrap_abstract(janet));
118     }
119     else static if(is(T == struct))
120     {
121         JanetTable* tbl = janet_unwrap_table(janet);
122         T newStruct;
123         for(int i=0;i<tbl.count;i++)
124         {
125             JanetKV kv = tbl.data[i];
126             static foreach(field; __traits(allMembers,T))
127             {
128                 if(getFromJanet!(typeof("T."~field))(kv.key) == field)
129                 {
130                     mixin("newStruct."~field) = getFromJanet!(typeof(mixin("T."~field)))(kv.value);
131                 }
132             }
133         }
134         return newStruct;
135     }
136     else static if(is(T==JanetCFunction))
137     {
138         return janet_unwrap_cfunction(janet);
139     }
140     else static if(isPointer!T)
141     {
142         return cast(T)(janet_unwrap_pointer(janet));
143     }
144 }
145 
146 /**
147     Wraps a D value to a Janet value.
148     Works for a bunch of built-in types as well as structs; for classes, see register.d.
149 */
150 Janet janetWrap(T,string strType = "string")(T x)
151 {
152     static if(is(T==Janet))
153     {
154         return x;
155     }
156     else static if(is(T == bool))
157     {
158         return janet_wrap_boolean(x);
159     }
160     else static if(is(T==void*))
161     {
162         return janet_wrap_abstract(x);
163     }
164     else static if(is(T == JanetCFunction))
165     {
166         return janet_wrap_cfunction(x);
167     }
168     else static if(isSomeString!T)
169     {
170         import std.string : toStringz;
171         final switch(strType)
172         {
173             case "string":
174                 return janet_wrap_string(janet_string(cast(ubyte*)x,cast(int)x.length));
175             case "symbol":
176                 return janet_wrap_symbol(janet_string(cast(ubyte*)x,cast(int)x.length));
177             case "keyword":
178                 return janet_wrap_keyword(janet_string(cast(ubyte*)x,cast(int)x.length));
179         }
180     }
181     else static if(is(T==const(char)*))
182     {
183         final switch(strType)
184         {
185             case "string":
186                 return janet_wrap_string(janet_cstring(x));
187             case "symbol":
188                 return janet_wrap_symbol(janet_cstring(x));
189             case "keyword":
190                 return janet_wrap_keyword(janet_cstring(x));
191         }
192     }
193     else static if(isPointer!T)
194     {
195         return janet_wrap_pointer(x);
196     }
197     else static if(is(T : double))
198     {
199         return janet_wrap_number_safe(x);
200     }
201     else static if(is(T : int))
202     {
203         return janet_wrap_integer(x);
204     }
205     else static if(isArray!T)
206     {
207         JanetArray* arr = janet_array(x.length);
208         foreach(item; x)
209         {
210             janet_array_push(arr,wrap(item));
211         }
212         return janet_wrap_array(arr);
213     }
214     else static if(is(T == struct))
215     {
216         JanetTable* tbl = janet_table(x.tupleof.length);
217         static foreach(field; __traits(allMembers,T))
218         {
219             static if(!isInternal!field &&
220                     field != "this" &&
221                     field != "opAssign") // also from LuaD
222             {
223                 enum isMemberFunction = mixin("is(typeof(&x." ~ field ~ ") == delegate)"); //LuaD
224                 static if(!isMemberFunction)
225                 {
226                     janet_table_put(tbl,janetWrap(field),mixin("janetWrap(x."~field~")"));
227                 }
228             }
229         }
230         return janet_wrap_table(tbl);
231     }
232     else static if(is(T == class))
233     {
234         //we're not using the GC here so we do the offset stuff manually... which is highly unsafe. annoying.
235         //seriously i think this might cause problems in the future. it's baffling that i might be doing this.
236         //for reference, janet_abstract_head(cast(void*)x) makes a janet_abstract_head object... a few bytes BEFORE the pointer.
237         //so if there's anything in the way, it'll just... overwrite it, i guess? it's not good.
238         //Upon further testing, this will fail after exactly 500 runs, without fail. This is not good.
239         return janet_wrap_abstract(new JanetDAbstractHead!(T)(x).ptr);
240     }
241     static assert("Not a compatible type for janet wrap!");
242 }
243 
244 Janet janetWrap(alias func)()
245 {
246     import janet.func : makeJanetCFunc;
247     return wrap(makeJanetCFunc!func);
248 }
249 
250 /// ditto
251 Janet janetWrap(K,V)(V[K] arr)
252 {
253     JanetTable* tbl = janet_table(arr.length);
254     foreach(K key,V value; arr)
255     {
256         janet_table_put(arr,janetWrap(key),wrjanetWrapap(value));
257     }
258     return janet_wrap_table(arr);
259 }
260 
261 unittest
262 {
263     import janet.vm : initJanet, coreEnv;
264     initJanet();
265     scope(exit) janet_deinit();
266     const string foo = "foo";
267     Janet* j;
268     auto janetFoo = janetWrap(foo);
269     import std.string : toStringz;
270     janet_def(coreEnv,toStringz("foo"),janetFoo,"A simple string, which should say 'foo'.");
271     const auto janetedString = getFromJanet!string(janetWrap(foo));
272     assert(janetedString == foo,janetedString~" is not "~foo~". This is likely caused by compiling with wrong settings (turn nanboxing off!)");
273 }