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 }