Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Use serializable structs as input to call method? #143

Closed
patata3000 opened this issue Mar 3, 2024 · 4 comments
Closed

[Question] Use serializable structs as input to call method? #143

patata3000 opened this issue Mar 3, 2024 · 4 comments

Comments

@patata3000
Copy link

patata3000 commented Mar 3, 2024

Hello, I'm trying to use nui plugin to create a popup window. I'm not sure about the way to do it.

I checked the mechanic example but it seems that it's not how I should use it.
I checked:

I have hard times connecting the dots. I may need to implement FromLua but I'm not sure.

I would like to get the Popup table from lua and call with PopupOpts as arguments.

#[derive(Serialize, Deserialize)]
struct PopupOpts {
    position: usize,
    enter: bool,
    focusable: bool,
    zindex: usize,
    // ...
}

impl FromObject for PopupOpts {
    fn from_object(obj: Object) -> Result<Self, ConversionError> {
        Self::deserialize(Deserializer::new(obj)).map_err(Into::into)
    }
}

impl ToObject for PopupOpts {
    fn to_object(self) -> Result<nvim_oxi::Object, nvim_oxi::conversion::Error> {
        self.serialize(Serializer::new()).map_err(Into::into)
    }
}

impl Pushable for PopupOpts {
    unsafe fn push(
        self,
        lstate: *mut lua::ffi::lua_State,
    ) -> Result<std::ffi::c_int, lua::Error> {
        self.to_object()
            .map_err(lua::Error::push_error_from_err::<Self, _>)?
            .push(lstate)
    }
}

impl lua::Poppable for PopupOpts {
    unsafe fn pop(lstate: *mut lua::ffi::lua_State) -> Result<Self, lua::Error> {
        let obj = Object::pop(lstate)?;
        Self::from_object(obj).map_err(lua::Error::pop_error_from_err::<Self, _>)
    }
}

pub fn show_content_popup(content: String) -> oxi::Result<()> {
    let nui = lua_require("nui")?;
    let popup: LuaTable = nui.get::<_, LuaTable>("popup")?;
    let popup_opts = PopupOpts {
        position: 0,
        enter: true,
        focusable: true,
        zindex: 50,
        // ...
    };
    // On next line, I get this error:
    // required for `ui::nui::popup::Popup` to implement `mlua::IntoLua<'_>`
    // required for `ui::nui::popup::Popup` to implement `mlua::IntoLuaMulti<'_>` [E0277]
    popup.call(popup_opts)?;
    Ok(())
}
@patata3000
Copy link
Author

patata3000 commented Mar 5, 2024

Ok I'm answering my own question. Time gave me open mindedness. This was something I kind of realised already but with new information, it got fuzzy in my head. I explain it here for myself and anybody else that could be interested.

So to interact in lua, you've got 2 ways (back and forth). Either:

  1. You need to run some Lua function, get back some lua values (tables, boolean, integers...) in your Rust code.
    Here, in general, you can use mlua crate. You get global lua state with lua.globals() and you require modules, functions, tables... that you can translate into Rust data types and interact with. So this is how you get Lua into Rust.
    However, what have done our dear friend here @noib3 is to give access to the nvim API. Being able to manipulate buffers, windows and whatnot in the nvim world without using the (kinda heavy) mlua interface (it's using C bindings directly)

  2. Lua calls some rust code that needs to be made accessible with #[nvim_oxi::module],
    For this part, we're using Rust from the Lua code. We get from nvim_oxi some nice bindings that let us convert Rust into Lua easily with: Function::from_fn, Object::from, Dictionary::from_iter...

Ok so now there is a bit more. When you expose a function using with Function::from_fn(my_func) , you may want to let the Lua code to send and receive some data as parameters to my_func (which is written in Rust).

  • You may want this data parameter to fill a Rust struct that you can use idiomatically in your Rust code.
  • You may want to return this data struct to Lua.
    This is why FromObject with Poppable and, ToObject with Pushable traits have been created. This is demonstrated in the mechanic example.

Now, mlua crate offers Rust code writers to implement FromLua, FromLuaMulti and IntoLua, IntoLuaMulti. IntoLua is the traits I need to implement if I want to be able to write this line:

popup.call(popup_opts)?;

Now I'm still a troubled man you see. Both FromLua (in mlua) and FromObject/Poppable (in nvim_oxi) use Serialize and Deserialize traits. I wish to know if there is a way to combine the implementations of the traits from nvim_oxi with the ones from mlua already. If not, would it be possible? Soon? At some point? Never? Does it even make sense?

About the answer of my question more specifically, this works nicely and it makes me think that my questions are probably not worth it:

impl<'lua> IntoLua<'lua> for PopupOpts {
    fn into_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> {
        lua.to_value(&self)
    }
}

@noib3
Copy link
Owner

noib3 commented Mar 5, 2024

As you noted, both our traits and the ones exposed by mlua can be implemented on top of the type's Serialize and Deserialize impls.

I wish to know if there is a way to combine the implementations of the traits from nvim_oxi with the ones from mlua already

If I understand you correctly you'd like to have something like this:

#[cfg(feature = "mlua")]
mod mlua_impls {
    impl<T: ToObject> IntoLua for T { .. }
    impl<T: FromObject> FromLua for T { .. }
}

unfortunately this results in a compiler error because of the orphan rule.

@patata3000
Copy link
Author

patata3000 commented Mar 5, 2024

Yes, it would be something like this but I don't know what would be the right way to do it.

And what about the other way around? Not sure it makes sense again

#[cfg(feature = "mlua")]
mod mlua_impls {
    impl<T: IntoLua> ToObject for T { .. }
    impl<T: FromLua> FromObject for T { .. }
}

@noib3
Copy link
Owner

noib3 commented Mar 5, 2024

We'd need specialization for that to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants