DelSkayn / rquickjs

High level bindings to the quickjs javascript engine
MIT License
431 stars 58 forks source link

Macro to define data objects #285

Open Sytten opened 3 months ago

Sytten commented 3 months ago

I had this idea since I often use "data objects" to get multiple inputs in a JS function.

Given:

#[derive(Clone, Trace)]
#[rquickjs::data]
pub struct FindingSpec {
    title: String,
}

This would automatically implements

impl<'js> FromJs<'js> for FindingSpec {
    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
        let obj = value.get::<Object<'js>>()?;
        Ok(Self {
            title: obj.get("title")?,
        })
    }
}

An alternative to also allow new FindingSpec() would be:

#[derive(Clone, Trace)]
#[rquickjs::class(data)]
pub struct FindingSpec {
    title: String,
}

Which would implement:

impl<'js> rquickjs::FromJs<'js> for FindingSpec
    where
        for<'a> rquickjs::class::impl_::CloneWrapper<'a, Self>:
            rquickjs::class::impl_::CloneTrait<Self>,
    {
        fn from_js(
            ctx: &rquickjs::Ctx<'js>,
            value: rquickjs::Value<'js>,
        ) -> rquickjs::Result<Self> {
            use rquickjs::class::impl_::CloneTrait;
            if let Ok(value) = rquickjs::class::Class::<Self>::from_js(ctx, value) {
               let borrow = value.try_borrow()?;
               return Ok(rquickjs::class::impl_::CloneWrapper(&*borrow).wrap_clone())
            }
            if let Ok(value) = value.get::<Object<'js>>() {
                return Ok(Self {
                   title: obj.get("title")?,
                })
            }
            Err(/* Unsure what goes here */)
        }
    }

Happy to implement it @DelSkayn if you like the idea!

DelSkayn commented 3 months ago

I do like the idea but I don't think it should be an attribute macro. I would think making it a drive macro, like #[derive(FromJs)] would be more idiomatic.

Sytten commented 3 months ago

Any preference on a pure data object vs class based?

DelSkayn commented 3 months ago

The class one conflicts with the existing class macro.

When you currently tag a class with the class attribute it will also implement FromJs. So a class with #[derive(FromJs)] and #[class] will cause an error for conflicting FromJs implementations.