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
19#[derive(Debug)]
23pub struct LuaState<'scope, 'template, 'env>(&'scope mut minijinja::State<'template, 'env>);
24
25impl<'scope, 'template, 'env> LuaState<'scope, 'template, 'env> {
26 fn state(&self) -> &minijinja::State<'template, 'env> {
27 self.0
28 }
29
30 fn state_mut(&mut self) -> &mut minijinja::State<'template, 'env> {
31 self.0
32 }
33}
34
35impl<'scope, 'template, 'env> From<&'scope mut minijinja::State<'template, 'env>>
36 for LuaState<'scope, 'template, 'env>
37{
38 fn from(value: &'scope mut minijinja::State<'template, 'env>) -> Self {
39 LuaState(value)
40 }
41}
42
43impl<'scope, 'template, 'env> From<LuaState<'scope, 'template, 'env>>
44 for &'scope mut minijinja::State<'template, 'env>
45{
46 fn from(value: LuaState<'scope, 'template, 'env>) -> Self {
47 value.0
48 }
49}
50
51impl<'scope, 'template, 'env> Deref for LuaState<'scope, 'template, 'env> {
52 type Target = minijinja::State<'template, 'env>;
53
54 fn deref(&self) -> &Self::Target {
55 self.0
56 }
57}
58
59impl<'scope, 'template, 'env> DerefMut for LuaState<'scope, 'template, 'env> {
60 fn deref_mut(&mut self) -> &mut Self::Target {
61 self.0
62 }
63}
64
65impl<'scope, 'template, 'env> fmt::Display for LuaState<'scope, 'template, 'env> {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "State")
68 }
69}
70
71impl<'scope, 'template, 'env> mlua::UserData for LuaState<'scope, 'template, 'env> {
72 fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
73 fields.add_meta_field("__name", "state");
74 }
75
76 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
77 methods.add_method("name", |_, this, ()| -> mlua::Result<String> {
79 Ok(this.state().name().to_string())
80 });
81
82 methods.add_method(
84 "auto_escape",
85 |_, this, ()| -> mlua::Result<LuaAutoEscape> { Ok(this.state().auto_escape().into()) },
86 );
87
88 methods.add_method(
90 "undefined_behavior",
91 |_, this, ()| -> mlua::Result<LuaUndefinedBehavior> {
92 Ok(this.state().undefined_behavior().into())
93 },
94 );
95
96 methods.add_method(
98 "current_block",
99 |_, this, ()| -> mlua::Result<Option<String>> {
100 Ok(this.state().current_block().map(|s| s.to_string()))
101 },
102 );
103
104 methods.add_method(
106 "lookup",
107 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
108 Ok(this
111 .state()
112 .lookup(&name)
113 .and_then(|v| minijinja_to_lua(lua, &v))
114 .unwrap_or_default())
115 },
116 );
117
118 methods.add_method_mut(
120 "call_macro",
121 |lua,
122 this,
123 (name, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
124 -> mlua::Result<String> {
125 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
126
127 this.state_mut()
128 .call_macro(&name, &args)
129 .map_err(mlua::Error::external)
130 },
131 );
132
133 methods.add_method_mut(
135 "render_block",
136 |_, this, block: mlua::BorrowedStr| -> mlua::Result<String> {
137 this.state_mut()
138 .render_block(&block)
139 .map_err(mlua::Error::external)
140 },
141 );
142
143 methods.add_method("exports", |_, this, ()| -> mlua::Result<Vec<String>> {
145 Ok(this
146 .state()
147 .exports()
148 .into_iter()
149 .map(|i| i.to_string())
150 .collect())
151 });
152
153 methods.add_method(
155 "known_variables",
156 |_, this, ()| -> mlua::Result<Vec<String>> {
157 Ok(this
158 .state()
159 .known_variables()
160 .into_iter()
161 .map(|i| i.to_string())
162 .collect())
163 },
164 );
165
166 methods.add_method_mut(
168 "apply_filter",
169 |lua,
170 this,
171 (filter, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
172 -> mlua::Result<mlua::MultiValue> {
173 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
174
175 this.state_mut()
178 .apply_filter(&filter, &args)
179 .map(|v| minijinja_to_lua(lua, &v).unwrap_or_default())
180 .map_err(mlua::Error::external)
181 },
182 );
183
184 methods.add_method_mut(
186 "perform_test",
187 |lua,
188 this,
189 (test, mut args): (mlua::BorrowedStr, mlua::MultiValue)|
190 -> mlua::Result<bool> {
191 let args: Vec<JinjaValue> = lua_args_to_minijinja(lua, &mut args, true);
192
193 this.state_mut()
194 .perform_test(&test, &args)
195 .map_err(mlua::Error::external)
196 },
197 );
198
199 methods.add_method_mut(
201 "format",
202 |lua, this, val: mlua::Value| -> mlua::Result<String> {
203 let val = lua_to_minijinja(lua, &val).unwrap_or_default();
204
205 this.state_mut().format(val).map_err(mlua::Error::external)
206 },
207 );
208
209 methods.add_method(
211 "fuel_levels",
212 |lua, this, ()| -> mlua::Result<mlua::Value> {
213 lua.to_value(&this.state().fuel_levels())
214 },
215 );
216
217 methods.add_method(
220 "get_temp",
221 |lua, this, name: mlua::BorrowedStr| -> mlua::Result<mlua::MultiValue> {
222 Ok(this
225 .state()
226 .get_temp(&name)
227 .and_then(|v| minijinja_to_lua(lua, &v))
228 .unwrap_or_default())
229 },
230 );
231
232 methods.add_method_mut(
234 "set_temp",
235 |lua,
236 this,
237 (name, val): (mlua::BorrowedStr, mlua::Value)|
238 -> mlua::Result<mlua::MultiValue> {
239 if let Some(val) = lua_to_minijinja(lua, &val) {
240 Ok(this
241 .state_mut()
242 .set_temp(&name, val)
243 .and_then(|v| minijinja_to_lua(lua, &v))
244 .unwrap_or_default())
245 } else {
246 Err(mlua::Error::FromLuaConversionError {
247 from: val.type_name(),
248 to: "minijinja::Value".to_string(),
249 message: None,
250 })
251 }
252 },
253 );
254 }
255}