GithubHelp home page GithubHelp logo

Comments (10)

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

Hi David,

Sounds like a good idea. I'll look into supporting that..

For now you could indeed serialize your value to json and use it in quickjs..

e.g.

this might not even be to different from how I may implement support for serde::Value it's just a bit more boilerplate code

    #[tokio::test]
    async fn test_json_arg() {
        let rt = init_test_rt();

        // init my javascript function
        rt.js_eval(None, Script::new("myFunc.js", r#"
                function myFunction(argObj) {
                    console.log("I got an %s", typeof argObj);
                    console.log("It looks like this %s", argObj);
                    return "hello " + argObj["key"];
                }
            "#)).await.ok().expect("myFunc failed to parse");

        // parse my obj to json
        let mut my_json_deserable_object = HashMap::new();
        my_json_deserable_object.insert("key", "value");
        let json = serde_json::to_string(&my_json_deserable_object).ok().expect("serializing failed");

        let func_res = rt.js_loop_realm(None, move |_rt, realm| {
            // this runs in the worker thread for the EventLoop so json String needs to be moved here
            // now we parse the json to a JsValueRef
            let js_obj = parse_q(realm, json.as_str()).ok().expect("parsing json failed");
            // then we can invoke the function with that js_obj as input
            // get the global obj as function container
            let global = get_global_q(realm);
// invoke the function
            let func_res = crate::quickjs_utils::functions::invoke_member_function_q(realm, &global, "myFunction", vec![js_obj]);
            //return the value out of the worker thread as JsValueFacade
            realm.to_js_value_facade(&func_res.ok().expect("func failed"))
        }).await;

        let jsv = func_res.ok().expect("got err");
        assert_eq!(jsv.stringify(), "String: hello value");

    }

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

Oh, easier version (I forgot there allready was a JsValueFacade::JsonStr)

    #[tokio::test]
    async fn test_json_arg2() {
        let rt = init_test_rt();

        // init my javascript function
        rt.js_eval(None, Script::new("myFunc.js", r#"
                function myFunction(argObj) {
                    console.log("I got an %s", typeof argObj);
                    console.log("It looks like this %s", argObj);
                    return "hello " + argObj["key"];
                }
            "#)).await.ok().expect("myFunc failed to parse");

        // parse my obj to json
        let mut my_json_deserable_object = HashMap::new();
        my_json_deserable_object.insert("key", "value");
        let json = serde_json::to_string(&my_json_deserable_object).ok().expect("serializing failed");

        let json_js_value_facade = JsValueFacade::JsonStr {json};

        let func_res = rt.js_function_invoke(None, &[], "myFunction", vec![json_js_value_facade]).await;

        let jsv = func_res.ok().expect("got err");
        assert_eq!(jsv.stringify(), "String: hello value");

    }

from quickjs_es_runtime.

dgildeh avatar dgildeh commented on September 22, 2024

Thanks for the follow up! I was going to say the first version looked like it had to go too low level so was going to reply back with a simpler solution like the one you just shared once I had another chance to try out the code tonight - thanks for beating me to it!

I think the ideal solution to implement this would be to extend the JsValueConvertable trait to have support for native serde_json objects (with types serde_json::Map and serde_json::Value etc.) using to_js_value_facade() method so it works consistently with other values/code examples.

Ideally it also won't require having to convert it from json -> string -> JsValue and back again on return as that feels like there may be a performance hit in that somewhere!

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

Hi, yes i agree, this should be handled natively..

I'll look into it

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

should be usable in 0.8.7

from quickjs_es_runtime.

dgildeh avatar dgildeh commented on September 22, 2024

Thanks for implementing this. Unfortunately even after reading the code I'm struggling on how to use this. I can't see a .to_js_value_facade() implementation for serde_json:Value so I can easily pass an array of Objects to the function, and not entirely sure how to convert it back using the new method you added (serde_value_to_js_value_adapter) without getting the realm object. I was hoping or thinking it would work something like below:

let rows Vec<serde_json::Map<String, serde_json::Value>> = [..]
let result = PLUGIN_RT
        .js_function_invoke(None, &[], &method_name, vec![rows.to_js_value_facade()])
        .await
        .unwrap();
let updated_rows: Vec<serde_json::Map<String, serde_json::Value>> = result.from_js_value_facade();

Can you show an example of how to call a function using the new code or add an implementation of to_js_value_facade for type serde_json::Value and serde_json::Map? Thanks!

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

Ah yes, I forgot the to_js_value_facade for serde::Value, I'll impl that later today

but to convert a Value to JsValueFacade you can just use,

JsValueFacade::SerdeValue { value: my_serde_value }

And I assumed everything you get from serde is a Value, also Maps so I only implemented Value

How did you get a Vec<serde_json::Map<String, serde_json::Value>> ?

Can you alter that to get a Vec<Value> ? should be the same in the end

calling the function shoudl then be a matter of

let v: Vec<serde::Value> = vec![];
        let js_value_facades: Vec<JsValueFacade> = v
            .into_iter()
            .map(|value| JsValueFacade::SerdeValue { value })
            .collect();
let result = PLUGIN_RT
        .js_function_invoke(None, &[], &method_name, js_value_facades)
        .await
        .unwrap();

The result will then be a JsValueFacade::JsObject (which is basicly a reference to a value in the PLUGIN_RT)

(the reason for making this a reference and not a Map struct is so it may be used as input again for a different function and pass the actual reference to the js object)

Later I can impl a get_serde_value in JsValueFacade::JsObject which works just like js_get_object but return a Value instead of a HashMap<String, JsValueFacade>

Hmm I seem to be missing a handy util for turning a JsValueFacade into a json string.. i'll get back to that later also, in my current projects I mostly just return a json string from my functions so I don't need the extra job in the EventLoop (and thus my result JsValueFacade is a JsValueFacade::String)

to be continued...

from quickjs_es_runtime.

dgildeh avatar dgildeh commented on September 22, 2024

Hi - thanks, with a couple of modifications that worked to pass the array in, but still need to get the serde_json array back from the result - I can see its returning a JsValueFacade::Array of JsValueFacade::Object but not sure how to transform that back to a Vec<serde_json::Map<String, serde_json::Value>> as there's no .get_array() method on the result.

    // Convert rows into JS values that can be passed to the QuickJS sandbox
    let js_value_facades = JsValueFacade::Array {
        val: rows
            .into_iter()
            .map(|value| JsValueFacade::SerdeValue {
                value: Value::Object(value),
            })
            .collect(),
    };

    println!("js_value_facades: {:?}", js_value_facades);

    // Run the plugin passing the array of results as a JSON object
    let result = PLUGIN_RT
        .js_function_invoke(None, &[], &method_name, vec![js_value_facades])
        .await
        .unwrap();

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

I'm working on it under HiRoFa/utils#8

my_js_value_facade_result.to_serde_value(rti).await should do the trick when i'm done

from quickjs_es_runtime.

andrieshiemstra avatar andrieshiemstra commented on September 22, 2024

my testcases with utils:0.5.7 using from/to Value and from/to json


    #[derive(Serialize)]
    #[derive(Deserialize)]
    #[serde(rename_all = "camelCase")]
    struct User {
        name: String,
        last_name: String,
    }

    #[tokio::test]
    async fn serde_tests_serialize() {
        let rtb: QuickJsRuntimeBuilder = QuickJsRuntimeBuilder::new();
        let rt = rtb.js_build();

        // init my function
        rt.js_eval(
            None,
            Script::new(
                "test.js",
                r#"
                function myTest(user) {
                    return {
                        name: "proc_" + user.name,
                        lastName: "proc_" + user.lastName
                    }
                }
                "#,
            ),
        )
            .await.expect("script failed");

        // create a user obj
        let test_user_input = User {
            last_name: "Anderson".to_string(),
            name: "Mister".to_string(),
        };

        let args = vec![JsValueFacade::from_serializable(&test_user_input).expect("could not serialize to JsValueFacade")];

        let res: JsValueFacade = rt
            .js_function_invoke(None, &[], "myTest", args)
            .await
            .expect("func failed");
        let rti = rt.js_get_runtime_facade_inner().upgrade().unwrap();

        let json_result = res
            .to_json_string(&*rti)
            .await
            .expect("could not serialize to json");

        assert_eq!(
            json_result.as_str(),
            r#"{"name":"proc_Mister","lastName":"proc_Anderson"}"#
        );

        // serialize back to user
        let user_output: User = serde_json::from_str(json_result.as_str()).unwrap();
        assert_eq!(user_output.name.as_str(), "proc_Mister");
        assert_eq!(user_output.last_name.as_str(), "proc_Anderson");
    }

    #[tokio::test]
    async fn serde_tests_value() {
        let rtb: QuickJsRuntimeBuilder = QuickJsRuntimeBuilder::new();
        let rt = rtb.js_build();

        // init my function
        rt.js_eval(
            None,
            Script::new(
                "test.js",
                r#"
                function myTest(user) {
                    return {
                        name: "proc_" + user.name,
                        lastName: "proc_" + user.lastName
                    }
                }
                "#,
            ),
        )
            .await.expect("script failed");

        // create a user obj
        let test_user_input = User {
            last_name: "Anderson".to_string(),
            name: "Mister".to_string(),
        };

        let input_value: serde_json::Value = serde_json::to_value(test_user_input).expect("could not to_value");
        let args = vec![JsValueFacade::SerdeValue {value: input_value}];

        let res: JsValueFacade = rt
            .js_function_invoke(None, &[], "myTest", args)
            .await
            .expect("func failed");
        let rti = rt.js_get_runtime_facade_inner().upgrade().unwrap();

        // as value
        let value_result: serde_json::Value = res
            .to_serde_value(&*rti)
            .await
            .expect("could not serialize to json");

        assert!(value_result.is_object());

        // serialize back to user
        let user_output: User = serde_json::from_value(value_result).unwrap();
        assert_eq!(user_output.name.as_str(), "proc_Mister");
        assert_eq!(user_output.last_name.as_str(), "proc_Anderson");
    }

from quickjs_es_runtime.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.