rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.73k stars 175 forks source link

Tracking issue for CustomType derive macro #820

Closed schungx closed 7 months ago

schungx commented 7 months ago

PR: https://github.com/rhaiscript/rhai/pull/817

schungx commented 7 months ago

@MavethGH This is the tracking issue for the derive macro.

In https://github.com/rhaiscript/rhai/pull/819 I have further refined it:

schungx commented 7 months ago

This is the example that I intend to put into the Book:

use rhai::{CustomType, TypeBuilder};            // <- necessary imports

#[derive(Debug, Clone)]                         // <- necessary for any custom type
#[derive(CustomType)]                           // <- auto-implement 'CustomType'
struct Foo {
    #[rhai_custom_type_skip]                    // <- not included
    dummy: i64,

    #[rhai_custom_type_readonly]                // <- only auto getter, no setter for 'bar'
    bar: i64,

    #[rhai_custom_type_name("emphasize")]
    baz: bool,                                  // <- auto getter/setter for 'baz'

    #[rhai_custom_type_set(Self::set_hello)]    // <- call custom setter for 'hello'
    hello: String                               // <- auto getter for 'hello'
}

impl Foo {
    pub fn set_hello(&mut self, value: String) {
        self.hello = if self.baz {
            let mut s = self.hello.clone();
            s.push_str(&value);
            for _ in 0..self.bar { s.push('!'); }
            s
        } else {
            value
        };
    }
}
schungx commented 7 months ago

Actually, I dislike how all that rhai_custom_type_ prefix is so long and cumbersome...

It might be better (or not) if we can do this:

#[derive(CustomType)]
struct Foo {
    #[rhai_type(skip)]
    dummy: i64,

    #[rhai_type(readonly)]
    bar: i64,

    #[rhai_type(name="emphasize")]
    baz: bool,

    #[rhai_type(set=Self::set_hello)]
    hello: String
}
schungx commented 7 months ago

In the latest drop I added support for tuple structs -- essentially the same code just tweaked.

Also, the type name is used for pretty-print. That should be slightly better than the default which is the type's full path name.

Docs are being added to the Book.

schungx commented 7 months ago

@MavethGH , the latest drop is now pretty complete.

I have decided to call all the attributes rhai_type_* which should not be too long, but long enough to avoid collisions with other traits.

rhai_type_name can be put on the type or any field to rename them.

rhai_type_extra can specify functions to call (with the TypeBuilder) where you can add new functions.

Also, rhai_type_get now expects a regular getter method with &self, while rhai_type_get_mut takes the standard Rhai getter method with &mut self.

The complete set of attributes are now:

Attribute Applies to Parameter Description
rhai_type_name type, field string expression use this name instead of the type/field name.
rhai_type_skip field none skip this field.
rhai_type_readonly field none only auto-generate getter, no setter.
rhai_type_get field function path use this getter function (with &self) instead of the auto-generated getter.
rhai_type_get_mut field function path use this getter function (with &mut self) instead of the auto-generated getter.
rhai_type_set field function path use this setter function instead of the auto-generated setter.
rhai_type_extra type function path call this function after building the type to add additional API's
schungx commented 7 months ago

In the end, I used a single attribute rhai_type with option fields to make it consistent with plugin module macros.

#[rhai_type(name = "foo", get = Self::get_foo, readonly)]
schungx commented 7 months ago

I'm leaving the last task, auto-derive enums, unimplemented for now.

I'm not sure a derived API is the best way to expose enums in a dynamic scripting language... since every value in Rhai is an unlimited enum.

schungx commented 7 months ago

I'll close off this issue now as I feel the feature is reasonably complete. Thanks for starting this @MavethGH !

In fact, the derive macro will be featured in the Book as the preferred method of registering the API of a type.