diff --git a/inst/include/quickjsr/JSValue_to_Cpp.hpp b/inst/include/quickjsr/JSValue_to_Cpp.hpp new file mode 100644 index 0000000..a7a3853 --- /dev/null +++ b/inst/include/quickjsr/JSValue_to_Cpp.hpp @@ -0,0 +1,52 @@ +#ifndef QUICKJSR_JSVALUE_TO_CPP_HPP +#define QUICKJSR_JSVALUE_TO_CPP_HPP + +#include +#include +#include +#include +#include + +namespace quickjsr { + template ::value>* = nullptr> + T JSValue_to_Cpp(JSContext* ctx, JSValue val) { + double res; + JS_ToFloat64(ctx, &res, val); + return res; + } + + template ::value>* = nullptr> + T JSValue_to_Cpp(JSContext* ctx, JSValue val) { + int32_t res; + JS_ToInt32(ctx, &res, val); + return res; + } + + template ::value>* = nullptr> + T JSValue_to_Cpp(JSContext* ctx, JSValue val) { + return static_cast(JS_ToBool(ctx, val)); + } + + template ::value>* = nullptr> + T JSValue_to_Cpp(JSContext* ctx, JSValue val) { + return JS_ToCString(ctx, val); + } + + template ::value>* = nullptr> + T JSValue_to_Cpp(JSContext* ctx, JSValue val) { + T res; + uint32_t len; + JSValue arr_len = JS_GetPropertyStr(ctx, val, "length"); + JS_ToUint32(ctx, &len, arr_len); + JS_FreeValue(ctx, arr_len); + for (uint32_t i = 0; i < len; i++) { + JSValue elem = JS_GetPropertyUint32(ctx, val, i); + res.push_back(JSValue_to_Cpp>(ctx, elem)); + JS_FreeValue(ctx, elem); + } + return res; + } + +} // namespace quickjsr + +#endif diff --git a/inst/include/quickjsr/JSValue_to_SEXP.hpp b/inst/include/quickjsr/JSValue_to_SEXP.hpp index 5d0c2cf..229986c 100644 --- a/inst/include/quickjsr/JSValue_to_SEXP.hpp +++ b/inst/include/quickjsr/JSValue_to_SEXP.hpp @@ -3,25 +3,45 @@ #include #include +#include namespace quickjsr { +// Forward declaration to allow for recursive calls +SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val); SEXP JSValue_to_SEXP_scalar(JSContext* ctx, JSValue val) { if (JS_IsBool(val)) { - return cpp11::as_sexp(static_cast(JS_ToBool(ctx, val))); + return cpp11::as_sexp(JSValue_to_Cpp(ctx, val)); } if (JS_IsNumber(val)) { - double res; - JS_ToFloat64(ctx, &res, val); - return cpp11::as_sexp(res); + return cpp11::as_sexp(JSValue_to_Cpp(ctx, val)); } if (JS_IsString(val)) { - return cpp11::as_sexp(JS_ToCString(ctx, val)); + return cpp11::as_sexp(JSValue_to_Cpp(ctx, val)); } return cpp11::as_sexp("Unsupported type"); } +SEXP JSValue_to_SEXP_vector(JSContext* ctx, JSValue val) { + JSValue elem = JS_GetPropertyUint32(ctx, val, 0); + SEXP rtn; + if (JS_IsBool(elem)) { + rtn = cpp11::as_sexp(JSValue_to_Cpp>(ctx, val)); + } else if (JS_IsNumber(elem)) { + rtn = cpp11::as_sexp(JSValue_to_Cpp>(ctx, val)); + } else if (JS_IsString(elem)) { + rtn = cpp11::as_sexp(JSValue_to_Cpp>(ctx, val)); + } else { + rtn = cpp11::as_sexp("Unsupported type"); + } + JS_FreeValue(ctx, elem); + return rtn; +} + SEXP JSValue_to_SEXP(JSContext* ctx, JSValue val) { + if (JS_IsArray(ctx, val)) { + return JSValue_to_SEXP_vector(ctx, val); + } // TODO: Implement array and object conversion return JSValue_to_SEXP_scalar(ctx, val); } diff --git a/inst/include/quickjsr/type_traits.hpp b/inst/include/quickjsr/type_traits.hpp new file mode 100644 index 0000000..42165d8 --- /dev/null +++ b/inst/include/quickjsr/type_traits.hpp @@ -0,0 +1,26 @@ +#ifndef QUICKJSR_TYPE_TRAITS_HPP +#define QUICKJSR_TYPE_TRAITS_HPP + +#include +#include + +namespace quickjsr { + template + struct is_std_vector : std::false_type {}; + template + struct is_std_vector> : std::true_type {}; + + template + struct value_type { + using type = typename std::decay_t; + }; + + template + struct value_type::value>> { + using type = typename std::decay_t::value_type; + }; + + template + using value_type_t = typename value_type::type; +} // namespace quickjsr +#endif diff --git a/inst/tinytest/test_data_conversion.R b/inst/tinytest/test_data_conversion.R index beba160..cefe416 100644 --- a/inst/tinytest/test_data_conversion.R +++ b/inst/tinytest/test_data_conversion.R @@ -27,3 +27,19 @@ expect_eq_jsonlite(list(list(a = 1, b = 2), list(c = 3, d = 4))) expect_eq_jsonlite(list(c("e", "f"), c("g", "h"))) expect_eq_jsonlite(list(list("e", "f"), list("g", "h"))) expect_eq_jsonlite(list(list(a = "e", b = "f"), list(c = "g", d = "h"))) + + +# Test that the full round-trip conversion is consistent. +expect_eq_jsonlite_full <- function(x) { + expect_equal(qjs_passthrough(x, FALSE), jsonlite::fromJSON(jsonlite::toJSON(x))) +} +expect_eq_jsonlite_full(1) +expect_eq_jsonlite_full(1:3) +expect_eq_jsonlite_full(c(1.5, 2.5)) + +expect_eq_jsonlite_full("a") +expect_eq_jsonlite_full(c("a", "b", "c")) + +expect_eq_jsonlite_full(TRUE) +expect_eq_jsonlite_full(FALSE) +expect_eq_jsonlite_full(c(TRUE, FALSE))