1use crate::error::{Error, Result};
2use crate::private::Sealed;
3use crate::userdata::{AnyUserData, MetaMethod};
4use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
5
6#[cfg(feature = "async")]
7use futures_util::future::{self, LocalBoxFuture};
8
9pub trait AnyUserDataExt<'lua>: Sealed {
11 fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V>;
13
14 fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()>;
16
17 fn call<A, R>(&self, args: A) -> Result<R>
21 where
22 A: IntoLuaMulti<'lua>,
23 R: FromLuaMulti<'lua>;
24
25 #[cfg(feature = "async")]
29 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
30 fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
31 where
32 A: IntoLuaMulti<'lua>,
33 R: FromLuaMulti<'lua> + 'lua;
34
35 fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
38 where
39 A: IntoLuaMulti<'lua>,
40 R: FromLuaMulti<'lua>;
41
42 #[cfg(feature = "async")]
49 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
50 fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
51 where
52 A: IntoLuaMulti<'lua>,
53 R: FromLuaMulti<'lua> + 'lua;
54
55 fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
63 where
64 A: IntoLuaMulti<'lua>,
65 R: FromLuaMulti<'lua>;
66
67 #[cfg(feature = "async")]
74 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
75 fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
76 where
77 A: IntoLuaMulti<'lua>,
78 R: FromLuaMulti<'lua> + 'lua;
79}
80
81impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
82 fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
83 let metatable = self.get_metatable()?;
84 match metatable.get::<Value>(MetaMethod::Index)? {
85 Value::Table(table) => table.raw_get(key),
86 Value::Function(func) => func.call((self.clone(), key)),
87 _ => Err(Error::runtime("attempt to index a userdata value")),
88 }
89 }
90
91 fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
92 let metatable = self.get_metatable()?;
93 match metatable.get::<Value>(MetaMethod::NewIndex)? {
94 Value::Table(table) => table.raw_set(key, value),
95 Value::Function(func) => func.call((self.clone(), key, value)),
96 _ => Err(Error::runtime("attempt to index a userdata value")),
97 }
98 }
99
100 fn call<A, R>(&self, args: A) -> Result<R>
101 where
102 A: IntoLuaMulti<'lua>,
103 R: FromLuaMulti<'lua>,
104 {
105 let metatable = self.get_metatable()?;
106 match metatable.get::<Value>(MetaMethod::Call)? {
107 Value::Function(func) => func.call((self.clone(), args)),
108 _ => Err(Error::runtime("attempt to call a userdata value")),
109 }
110 }
111
112 #[cfg(feature = "async")]
113 fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
114 where
115 A: IntoLuaMulti<'lua>,
116 R: FromLuaMulti<'lua> + 'lua,
117 {
118 let metatable = match self.get_metatable() {
119 Ok(metatable) => metatable,
120 Err(err) => return Box::pin(future::err(err)),
121 };
122 match metatable.get::<Value>(MetaMethod::Call) {
123 Ok(Value::Function(func)) => {
124 let mut args = match args.into_lua_multi(self.0.lua) {
125 Ok(args) => args,
126 Err(e) => return Box::pin(future::err(e)),
127 };
128 args.push_front(Value::UserData(self.clone()));
129 Box::pin(async move { func.call_async(args).await })
130 }
131 Ok(_) => Box::pin(future::err(Error::runtime(
132 "attempt to call a userdata value",
133 ))),
134 Err(err) => Box::pin(future::err(err)),
135 }
136 }
137
138 fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
139 where
140 A: IntoLuaMulti<'lua>,
141 R: FromLuaMulti<'lua>,
142 {
143 self.call_function(name, (self.clone(), args))
144 }
145
146 #[cfg(feature = "async")]
147 fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
148 where
149 A: IntoLuaMulti<'lua>,
150 R: FromLuaMulti<'lua> + 'lua,
151 {
152 self.call_async_function(name, (self.clone(), args))
153 }
154
155 fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
156 where
157 A: IntoLuaMulti<'lua>,
158 R: FromLuaMulti<'lua>,
159 {
160 match self.get(name)? {
161 Value::Function(func) => func.call(args),
162 val => {
163 let msg = format!("attempt to call a {} value", val.type_name());
164 Err(Error::runtime(msg))
165 }
166 }
167 }
168
169 #[cfg(feature = "async")]
170 fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
171 where
172 A: IntoLuaMulti<'lua>,
173 R: FromLuaMulti<'lua> + 'lua,
174 {
175 match self.get(name) {
176 Ok(Value::Function(func)) => {
177 let args = match args.into_lua_multi(self.0.lua) {
178 Ok(args) => args,
179 Err(e) => return Box::pin(future::err(e)),
180 };
181 Box::pin(async move { func.call_async(args).await })
182 }
183 Ok(val) => {
184 let msg = format!("attempt to call a {} value", val.type_name());
185 Box::pin(future::err(Error::runtime(msg)))
186 }
187 Err(err) => Box::pin(future::err(err)),
188 }
189 }
190}