[Question] Use serializable structs as input to call method?
Closed this issue · 4 comments
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:
- https://github.com/mlua-rs/mlua/blob/master/examples/guided_tour.rs
- https://github.com/mlua-rs/mlua/blob/master/examples/serialize.rs
- https://github.com/mlua-rs/mlua/blob/master/examples/userdata.rs
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(())
}
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:
-
You need to run some Lua function, get back some lua values (tables, boolean, integers...) in your Rust code.
Here, in general, you can usemlua
crate. You getglobal
lua state withlua.globals()
and yourequire
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) -
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 fromnvim_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 whyFromObject
withPoppable
and,ToObject
withPushable
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)
}
}
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 frommlua
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.
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 { .. }
}
We'd need specialization for that to work.