Attribute Macro rquickjs::bind[][src]

#[bind]

An attribute to generate bindings easy

This macro allows register Rust constants, functions, data types and modules to use it from JavaScript.

NOTE: To export any nested items it should be public.

Supported attributes

Any attributes which is enclosed by a #[quickjs(...)] will be interpreted by this macro to control bindings.

Macro attributes

AttributeDescription
ident = "MyModule"The name of target unit struct to export
public, public = "self/super/crate"Makes the target unit struct visible
moduleAdds the ModuleDef impl to use bindings as ES6 module
objectAdds the ObjectDef impl for attaching bindings to an object
init, init = "js_module_init"Adds the js_module_init function (in particular for creating dynamically loadable modules or static libraries to use from C)
crate = "rquickjs"Allows rename rquickjs crate

Module attributes

AttributeDescription
rename = "new_name"Renames module to export
bareExports contents of the module to the parent module instead of creating submodule (this is off by default)
skipSkips exporting this module
hideDo not output this module (bindings only)

Constant attributes

AttributeDescription
rename = "new_name"Renames constant to export
valueDefines a property
writableMakes property to be writable
configurableMakes property to be configurable
enumerableMakes property to be enumerable
protoSets constant or property to prototype
skipSkips exporting this contant
hideDo not output this constant (bindings only)

Function attributes

AttributeDescription
rename = "new_name"Renames function to export
getUses function as a getter for a property
setUses function as a setter for a property
configurableMakes property to be configurable
enumerableMakes property to be enumerable
constructor, constructor = trueForces creating contructor
constructor = falseDisables creating contructor
skipSkips exporting this function
hideDo not output this function (bindings only)

When multiple functions is declared with same name (i.e. same rename attribute value) it will be overloaded. The overloading rules is dead simple, so currently you should be care to get it works. Overloading is not supported for property getters/setters.

Data type attributes

This attributes applies to structs and enums to use it as ES6 classes.

AttributeDescription
rename = "new_name"Renames data type to export
has_refsMarks data which has internal refs to other JS values (requires HasRefs to be implemented)
cloneableMarks data type which implements Clone trait
skipSkips exporting this data type
hideDo not output this data type (bindings only)

The following traits will be implemented for data type:

The following traits will be implemented for references to data type:

Data field attributes

This attributes applies to data fields to use it as a properties.

AttributeDescription
rename = "new_name"Renames field to export
readonlyMakes this field to be readonly
skipSkips exporting this field

impl block attributes

This attributes applies to impl blocks to bind class methods and properties and also adding static constants and functions.

AttributeDescription
rename = "new_name"Renames data type to export
has_refsMarks data which has internal refs to other JS values (requires HasRefs to be implemented)
skipSkips exporting this impl block
hideDo not output this impl block (bindings only)

Examples

Single function binding

use rquickjs::{Runtime, Context, bind};

#[bind(object)]
pub fn add2(a: f32, b: f32) -> f32 {
    a + b
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

ctx.with(|ctx| {
    let glob = ctx.globals();
    glob.init_def::<Add2>().unwrap();

    let res: f32 = ctx.eval(r#"add2(1, 2)"#).unwrap();
    assert_eq!(res, 3.0);
});

Module with two functions

use rquickjs::{Runtime, Context, Object, bind};

#[bind(object)]
pub mod math {
    pub const PI: f32 = core::f32::consts::PI;

    pub fn add2(a: f32, b: f32) -> f32 {
        a + b
    }

    pub fn mul2(a: f32, b: f32) -> f32 {
        a * b
    }
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

ctx.with(|ctx| {
    let glob = ctx.globals();
    glob.init_def::<Math>().unwrap();

    let res: f32 = ctx.eval(r#"math.mul2(3, math.add2(1, 2))"#).unwrap();
    assert_eq!(res, 9.0);
});

Module with two functions which reused from another module

use rquickjs::{Runtime, Context, Object, bind};

mod my_math {
    pub const PI: f32 = core::f32::consts::PI;

    pub fn add2(a: f32, b: f32) -> f32 {
        a + b
    }

    pub fn mul2(a: f32, b: f32) -> f32 {
        a * b
    }
}

#[bind(object)]
mod math {
    pub use super::my_math::*;

    #[quickjs(hide)]
    pub const PI: f32 = ();

    #[quickjs(hide)]
    pub fn add2(a: f32, b: f32) -> f32 {}

    #[quickjs(hide)]
    pub fn mul2(a: f32, b: f32) -> f32 {}
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

ctx.with(|ctx| {
    let glob = ctx.globals();
    glob.init_def::<Math>().unwrap();

    let res: f32 = ctx.eval(r#"math.mul2(3, math.add2(1, 2))"#).unwrap();
    assert_eq!(res, 9.0);
});

Bare module definition

use rquickjs::{Runtime, Context, Object, bind};

#[bind(object)]
#[quickjs(bare)]
pub mod math {
    #[quickjs(name = "pi")]
    pub const PI: f32 = core::f32::consts::PI;

    pub fn add2(a: f32, b: f32) -> f32 {
        a + b
    }

    pub fn mul2(a: f32, b: f32) -> f32 {
        a * b
    }
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

ctx.with(|ctx| {
    let glob = ctx.globals();
    glob.init_def::<Math>().unwrap();

    let res: f32 = ctx.eval(r#"mul2(3, add2(1, 2))"#).unwrap();
    assert_eq!(res, 9.0);
});

Async function binding

use rquickjs::{Runtime, Context, Promise, bind, AsyncStd};

#[bind(object)]
pub async fn sleep(msecs: u64) {
    async_std::task::sleep(
        std::time::Duration::from_millis(msecs)
    ).await;
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

rt.spawn_executor(AsyncStd);

ctx.with(|ctx| {
    ctx.globals().init_def::<Sleep>().unwrap();
});

let promise: Promise<String> = ctx.with(|ctx| {
    ctx.eval(r#"
        async function mysleep() {
            await sleep(50);
            return "ok";
        }
        mysleep()
    "#).unwrap()
});

let res = promise.await.unwrap();
assert_eq!(res, "ok");

rt.idle().await;

Class binding

use rquickjs::{bind, Runtime, Context, Error};

#[bind(object)]
#[quickjs(bare)]
mod geom {
    pub struct Point {
        // field properties
        pub x: f64,
        pub y: f64,
    }

    impl Point {
        // constructor
        pub fn new(x: f64, y: f64) -> Self {
            Self { x, y }
        }

        // instance method
        pub fn norm(&self) -> f64 {
            Self::dot(self, self).sqrt()
        }

        // instance property getter
        #[quickjs(get, enumerable)]
        pub fn xy(&self) -> (f64, f64) {
            (self.x, self.y)
        }

        // instance property setter
        #[quickjs(rename = "xy", set)]
        pub fn set_xy(&mut self, xy: (f64, f64)) {
            self.x = xy.0;
            self.y = xy.1;
        }

        // static method
        pub fn dot(a: &Point, b: &Point) -> f64 {
            a.x * b.x + a.y * b.y
        }

        // static property with getter
        #[quickjs(get)]
        pub fn zero() -> Self {
            Point { x: 0.0, y: 0.0 }
        }
    }
}

let rt = Runtime::new().unwrap();
let ctx = Context::full(&rt).unwrap();

ctx.with(|ctx| {
    ctx.globals().init_def::<Geom>().unwrap();
});

ctx.with(|ctx| {
    ctx.eval::<(), _>(r#"
        function assert(res) {
            if (!res) throw new Error("Assertion failed");
        }

        class ColorPoint extends Point {
            constructor(x, y, color) {
            super(x, y);
                this.color = color;
            }
            get_color() {
                return this.color;
            }
        }

        let pt = new Point(2, 3);
        assert(pt.x === 2);
        assert(pt.y === 3);
        pt.x = 4;
        assert(pt.x === 4);
        assert(pt.norm() == 5);
        let xy = pt.xy;
        assert(xy.length === 2);
        assert(xy[0] === 4);
        assert(xy[1] === 3);
        pt.xy = [3, 4];
        assert(pt.x === 3);
        assert(pt.y === 4);
        assert(Point.dot(pt, Point(2, 1)) == 10);

        let ptz = Point.zero;
        assert(ptz.x === 0);
        assert(ptz.y === 0);

        let ptc = new ColorPoint(2, 3, 0xffffff);
        assert(ptc.x === 2);
        assert(ptc.color === 0xffffff);
        assert(ptc.get_color() === 0xffffff);
    "#).unwrap();
});