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 |lua, this, ()| -> mlua::Result<mlua::Value> {
165 let ae: LuaAutoEscape = this.state().auto_escape().into();
166 lua.to_value(&ae)
167 },
168 );
169
170 methods.add_method(
172 "undefined_behavior",
173 |lua, this, ()| -> mlua::Result<mlua::Value> {
174 let ub: LuaUndefinedBehavior = this.state().undefined_behavior().into();
175 lua.to_value(&ub)
176 },
177 );
178
179 methods.add_method(
181 "current_block",
182 |_, this, ()| -> mlua::Result<Option<String>> {
183 Ok(this.state().current_block().map(|s| s.to_string()))
184 },
185 );
186
187 methods.add_method(
189 "lookup",
190 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
191 Ok(this
194 .state()
195 .lookup(&name)
196 .and_then(|v| minijinja_to_lua(lua, &v))
197 .unwrap_or_default())
198 },
199 );
200
201 methods.add_method(
203 "call_macro",
204 |lua,
205 this,
206 (name, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
207 -> mlua::Result<String> {
208 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
209
210 this.state()
211 .call_macro(&name, &args)
212 .map_err(mlua::Error::external)
213 },
214 );
215
216 methods.add_method("exports", |_, this, ()| -> mlua::Result<Vec<String>> {
218 Ok(this
219 .state()
220 .exports()
221 .into_iter()
222 .map(|i| i.to_string())
223 .collect())
224 });
225
226 methods.add_method(
228 "known_variables",
229 |_, this, ()| -> mlua::Result<Vec<String>> {
230 Ok(this
231 .state()
232 .known_variables()
233 .into_iter()
234 .map(|i| i.to_string())
235 .collect())
236 },
237 );
238
239 methods.add_method(
241 "apply_filter",
242 |lua,
243 this,
244 (filter, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
245 -> mlua::Result<mlua::MultiValue> {
246 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
247
248 this.state()
251 .apply_filter(&filter, &args)
252 .map(|v| minijinja_to_lua(lua, &v).unwrap_or_default())
253 .map_err(mlua::Error::external)
254 },
255 );
256
257 methods.add_method(
259 "perform_test",
260 |lua,
261 this,
262 (test, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
263 -> mlua::Result<bool> {
264 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
265
266 this.state()
267 .perform_test(&test, &args)
268 .map_err(mlua::Error::external)
269 },
270 );
271
272 methods.add_method(
274 "format",
275 |lua, this, val: mlua::Value| -> mlua::Result<String> {
276 let val = lua_to_minijinja(lua, &val).unwrap_or_default();
277
278 this.state().format(val).map_err(mlua::Error::external)
279 },
280 );
281
282 methods.add_method(
284 "fuel_levels",
285 |lua, this, ()| -> mlua::Result<mlua::Value> { lua.to_value(&this.state().fuel_levels()) },
286 );
287
288 methods.add_method(
291 "get_temp",
292 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
293 Ok(this
296 .state()
297 .get_temp(&name)
298 .and_then(|v| minijinja_to_lua(lua, &v))
299 .unwrap_or_default())
300 },
301 );
302
303 methods.add_method(
305 "set_temp",
306 |lua,
307 this,
308 (name, val): (mlua::BorrowedStr, mlua::Value)|
309 -> mlua::Result<mlua::MultiValue> {
310 if let Some(val) = lua_to_minijinja(lua, &val) {
311 Ok(this
312 .state()
313 .set_temp(&name, val)
314 .and_then(|v| minijinja_to_lua(lua, &v))
315 .unwrap_or_default())
316 } else {
317 Err(mlua::Error::FromLuaConversionError {
318 from: val.type_name(),
319 to: "minijinja::Value".to_string(),
320 message: None,
321 })
322 }
323 },
324 );
325
326 methods.add_method(
328 "get_or_set_temp",
329 |lua,
330 this,
331 (name, func): (mlua::BorrowedStr, mlua::Function)|
332 -> mlua::Result<mlua::MultiValue> {
333 let val = match this.state().get_temp(&name) {
334 Some(v) => v,
335 None => {
336 let val = func.call::<mlua::Value>(mlua::Value::Nil)?;
337
338 if let Some(val) = lua_to_minijinja(lua, &val) {
339 this.state().set_temp(&name, val.clone());
340 val
341 } else {
342 return Err(mlua::Error::FromLuaConversionError {
343 from: val.type_name(),
344 to: "minijinja::Value".to_string(),
345 message: None,
346 });
347 }
348 },
349 };
350
351 Ok(minijinja_to_lua(lua, &val).unwrap_or_default())
352 },
353 );
354}