1use std::{
4 fmt,
5 ops::{Deref, DerefMut},
6};
7
8use minijinja::Value as JinjaValue;
9use mlua::LuaSerdeExt;
10
11use crate::convert::{
12 LuaAutoEscape,
13 LuaUndefinedBehavior,
14 lua_args_to_minijinja,
15 lua_to_minijinja,
16 minijinja_to_lua,
17};
18
19pub(crate) trait LuaState<'template, 'env> {
20 fn state(&self) -> &minijinja::State<'template, 'env>;
21}
22
23#[derive(Debug)]
27pub struct LuaStateRef<'scope, 'template, 'env>(&'scope minijinja::State<'template, 'env>);
28
29impl<'scope, 'template, 'env> From<&'scope minijinja::State<'template, 'env>>
30 for LuaStateRef<'scope, 'template, 'env>
31{
32 fn from(value: &'scope minijinja::State<'template, 'env>) -> Self {
33 LuaStateRef(value)
34 }
35}
36
37impl<'scope, 'template, 'env> From<LuaStateRef<'scope, 'template, 'env>>
38 for &'scope minijinja::State<'template, 'env>
39{
40 fn from(value: LuaStateRef<'scope, 'template, 'env>) -> Self {
41 value.0
42 }
43}
44
45impl<'scope, 'template, 'env> Deref for LuaStateRef<'scope, 'template, 'env> {
46 type Target = minijinja::State<'template, 'env>;
47
48 fn deref(&self) -> &Self::Target {
49 self.0
50 }
51}
52
53impl<'scope, 'template, 'env> LuaState<'template, 'env> for LuaStateRef<'scope, 'template, 'env> {
54 fn state(&self) -> &minijinja::State<'template, 'env> {
55 self.0
56 }
57}
58
59impl<'scope, 'template, 'env> fmt::Display for LuaStateRef<'scope, 'template, 'env> {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "State")
62 }
63}
64
65impl<'scope, 'template, 'env> mlua::UserData for LuaStateRef<'scope, 'template, 'env> {
66 fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
67 fields.add_meta_field("__name", "state");
68 }
69
70 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
71 add_common_methods(methods);
72 }
73}
74
75#[derive(Debug)]
79pub struct LuaStateMut<'scope, 'template, 'env>(&'scope mut minijinja::State<'template, 'env>);
80
81impl<'scope, 'template, 'env> LuaStateMut<'scope, 'template, 'env> {
82 fn state_mut(&mut self) -> &mut minijinja::State<'template, 'env> {
83 self.0
84 }
85}
86
87impl<'scope, 'template, 'env> From<&'scope mut minijinja::State<'template, 'env>>
88 for LuaStateMut<'scope, 'template, 'env>
89{
90 fn from(value: &'scope mut minijinja::State<'template, 'env>) -> Self {
91 LuaStateMut(value)
92 }
93}
94
95impl<'scope, 'template, 'env> From<LuaStateMut<'scope, 'template, 'env>>
96 for &'scope mut minijinja::State<'template, 'env>
97{
98 fn from(value: LuaStateMut<'scope, 'template, 'env>) -> Self {
99 value.0
100 }
101}
102
103impl<'scope, 'template, 'env> Deref for LuaStateMut<'scope, 'template, 'env> {
104 type Target = minijinja::State<'template, 'env>;
105
106 fn deref(&self) -> &Self::Target {
107 self.0
108 }
109}
110
111impl<'scope, 'template, 'env> DerefMut for LuaStateMut<'scope, 'template, 'env> {
112 fn deref_mut(&mut self) -> &mut Self::Target {
113 self.0
114 }
115}
116
117impl<'scope, 'template, 'env> LuaState<'template, 'env> for LuaStateMut<'scope, 'template, 'env> {
118 fn state(&self) -> &minijinja::State<'template, 'env> {
119 self.0
120 }
121}
122
123impl<'scope, 'template, 'env> fmt::Display for LuaStateMut<'scope, 'template, 'env> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "State")
126 }
127}
128
129impl<'scope, 'template, 'env> mlua::UserData for LuaStateMut<'scope, 'template, 'env> {
130 fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
131 fields.add_meta_field("__name", "state");
132 }
133
134 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
135 add_common_methods(methods);
136
137 methods.add_method_mut(
139 "render_block",
140 |_, this, block: mlua::BorrowedStr| -> mlua::Result<String> {
141 this.state_mut()
142 .render_block(&block)
143 .map_err(mlua::Error::external)
144 },
145 );
146 }
147}
148
149fn add_common_methods<'template, 'env, S, M>(methods: &mut M)
151where
152 S: LuaState<'template, 'env>,
153 M: mlua::UserDataMethods<S>,
154 'env: 'template,
155{
156 methods.add_method("name", |_, this, ()| -> mlua::Result<String> {
158 Ok(this.state().name().to_string())
159 });
160
161 methods.add_method(
163 "auto_escape",
164 |_, this, ()| -> mlua::Result<LuaAutoEscape> { Ok(this.state().auto_escape().into()) },
165 );
166
167 methods.add_method(
169 "undefined_behavior",
170 |_, this, ()| -> mlua::Result<LuaUndefinedBehavior> {
171 Ok(this.state().undefined_behavior().into())
172 },
173 );
174
175 methods.add_method(
177 "current_block",
178 |_, this, ()| -> mlua::Result<Option<String>> {
179 Ok(this.state().current_block().map(|s| s.to_string()))
180 },
181 );
182
183 methods.add_method(
185 "lookup",
186 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
187 Ok(this
190 .state()
191 .lookup(&name)
192 .and_then(|v| minijinja_to_lua(lua, &v))
193 .unwrap_or_default())
194 },
195 );
196
197 methods.add_method(
199 "call_macro",
200 |lua,
201 this,
202 (name, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
203 -> mlua::Result<String> {
204 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
205
206 this.state()
207 .call_macro(&name, &args)
208 .map_err(mlua::Error::external)
209 },
210 );
211
212 methods.add_method("exports", |_, this, ()| -> mlua::Result<Vec<String>> {
214 Ok(this
215 .state()
216 .exports()
217 .into_iter()
218 .map(|i| i.to_string())
219 .collect())
220 });
221
222 methods.add_method(
224 "known_variables",
225 |_, this, ()| -> mlua::Result<Vec<String>> {
226 Ok(this
227 .state()
228 .known_variables()
229 .into_iter()
230 .map(|i| i.to_string())
231 .collect())
232 },
233 );
234
235 methods.add_method(
237 "apply_filter",
238 |lua,
239 this,
240 (filter, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
241 -> mlua::Result<mlua::MultiValue> {
242 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
243
244 this.state()
247 .apply_filter(&filter, &args)
248 .map(|v| minijinja_to_lua(lua, &v).unwrap_or_default())
249 .map_err(mlua::Error::external)
250 },
251 );
252
253 methods.add_method(
255 "perform_test",
256 |lua,
257 this,
258 (test, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
259 -> mlua::Result<bool> {
260 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
261
262 this.state()
263 .perform_test(&test, &args)
264 .map_err(mlua::Error::external)
265 },
266 );
267
268 methods.add_method(
270 "format",
271 |lua, this, val: mlua::Value| -> mlua::Result<String> {
272 let val = lua_to_minijinja(lua, &val).unwrap_or_default();
273
274 this.state().format(val).map_err(mlua::Error::external)
275 },
276 );
277
278 methods.add_method(
280 "fuel_levels",
281 |lua, this, ()| -> mlua::Result<mlua::Value> { lua.to_value(&this.state().fuel_levels()) },
282 );
283
284 methods.add_method(
287 "get_temp",
288 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
289 Ok(this
292 .state()
293 .get_temp(&name)
294 .and_then(|v| minijinja_to_lua(lua, &v))
295 .unwrap_or_default())
296 },
297 );
298
299 methods.add_method(
301 "set_temp",
302 |lua,
303 this,
304 (name, val): (mlua::BorrowedStr, mlua::Value)|
305 -> mlua::Result<mlua::MultiValue> {
306 if let Some(val) = lua_to_minijinja(lua, &val) {
307 Ok(this
308 .state()
309 .set_temp(&name, val)
310 .and_then(|v| minijinja_to_lua(lua, &v))
311 .unwrap_or_default())
312 } else {
313 Err(mlua::Error::FromLuaConversionError {
314 from: val.type_name(),
315 to: "minijinja::Value".to_string(),
316 message: None,
317 })
318 }
319 },
320 );
321
322 methods.add_method(
324 "get_or_set_temp",
325 |lua,
326 this,
327 (name, func): (mlua::BorrowedStr, mlua::Function)|
328 -> mlua::Result<mlua::MultiValue> {
329 let val = match this.state().get_temp(&name) {
330 Some(v) => v,
331 None => {
332 let val = func.call::<mlua::Value>(mlua::Value::Nil)?;
333
334 if let Some(val) = lua_to_minijinja(lua, &val) {
335 this.state().set_temp(&name, val.clone());
336 val
337 } else {
338 return Err(mlua::Error::FromLuaConversionError {
339 from: val.type_name(),
340 to: "minijinja::Value".to_string(),
341 message: None,
342 });
343 }
344 },
345 };
346
347 Ok(minijinja_to_lua(lua, &val).unwrap_or_default())
348 },
349 );
350}