Skip to content

Commit 1b8c71b

Browse files
committed
Add render::common module
This stores `Resolve` and `Render` impls for top-level elements like `Variable`, `TokenTree` and `Text` and elements used by both tags and filters, like `Argument` and `TagElement`.
1 parent e1ed6c5 commit 1b8c71b

File tree

3 files changed

+272
-261
lines changed

3 files changed

+272
-261
lines changed

src/render.rs

+2-260
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1+
pub mod common;
12
pub mod filters;
23
pub mod tags;
34
pub mod types;
45

56
use std::borrow::Cow;
6-
use std::collections::HashMap;
77

88
use pyo3::prelude::*;
99

10-
use crate::error::{PyRenderError, RenderError};
11-
use crate::parse::{TagElement, TokenTree};
12-
use crate::types::Argument;
13-
use crate::types::ArgumentType;
10+
use crate::error::PyRenderError;
1411
use crate::types::TemplateString;
15-
use crate::types::Text;
16-
use crate::types::Variable;
1712
use types::{Content, Context};
1813

1914
pub type ResolveResult<'t, 'py> = Result<Option<Content<'t, 'py>>, PyRenderError>;
@@ -58,256 +53,3 @@ where
5853
}
5954
}
6055
}
61-
62-
impl Resolve for Variable {
63-
fn resolve<'t, 'py>(
64-
&self,
65-
py: Python<'py>,
66-
template: TemplateString<'t>,
67-
context: &mut Context,
68-
) -> ResolveResult<'t, 'py> {
69-
let mut parts = self.parts(template);
70-
let (first, mut object_at) = parts.next().expect("Variable names cannot be empty");
71-
let mut variable = match context.context.get(first) {
72-
Some(variable) => variable.bind(py).clone(),
73-
None => return Ok(None),
74-
};
75-
76-
for (part, key_at) in parts {
77-
variable = match variable.get_item(part) {
78-
Ok(variable) => variable,
79-
Err(_) => match variable.getattr(part) {
80-
Ok(variable) => variable,
81-
Err(_) => {
82-
let int = match part.parse::<usize>() {
83-
Ok(int) => int,
84-
Err(_) => {
85-
return Err(RenderError::VariableDoesNotExist {
86-
key: part.to_string(),
87-
object: variable.str()?.to_string(),
88-
key_at: key_at.into(),
89-
object_at: Some(object_at.into()),
90-
}
91-
.into())
92-
}
93-
};
94-
match variable.get_item(int) {
95-
Ok(variable) => variable,
96-
Err(_) => todo!(),
97-
}
98-
}
99-
},
100-
};
101-
object_at.1 += key_at.1 + 1;
102-
}
103-
Ok(Some(Content::Py(variable)))
104-
}
105-
}
106-
107-
impl Resolve for Text {
108-
fn resolve<'t, 'py>(
109-
&self,
110-
_py: Python<'py>,
111-
template: TemplateString<'t>,
112-
context: &mut Context,
113-
) -> ResolveResult<'t, 'py> {
114-
let resolved = Cow::Borrowed(template.content(self.at));
115-
Ok(Some(match context.autoescape {
116-
false => Content::String(resolved),
117-
true => Content::HtmlSafe(resolved),
118-
}))
119-
}
120-
}
121-
122-
impl Resolve for Argument {
123-
fn resolve<'t, 'py>(
124-
&self,
125-
py: Python<'py>,
126-
template: TemplateString<'t>,
127-
context: &mut Context,
128-
) -> ResolveResult<'t, 'py> {
129-
Ok(Some(match &self.argument_type {
130-
ArgumentType::Text(text) => return text.resolve(py, template, context),
131-
ArgumentType::TranslatedText(_text) => todo!(),
132-
ArgumentType::Variable(variable) => match variable.resolve(py, template, context)? {
133-
Some(content) => content,
134-
None => {
135-
let key = template.content(variable.at).to_string();
136-
let context: HashMap<&String, &Bound<'py, PyAny>> = context
137-
.context
138-
.iter()
139-
.map(|(k, v)| (k, v.bind(py)))
140-
.collect();
141-
let object = format!("{:?}", context);
142-
return Err(RenderError::VariableDoesNotExist {
143-
key,
144-
object,
145-
key_at: variable.at.into(),
146-
object_at: None,
147-
}
148-
.into());
149-
}
150-
},
151-
ArgumentType::Float(number) => Content::Float(*number),
152-
ArgumentType::Int(number) => Content::Int(number.clone()),
153-
}))
154-
}
155-
}
156-
157-
impl Resolve for TagElement {
158-
fn resolve<'t, 'py>(
159-
&self,
160-
py: Python<'py>,
161-
template: TemplateString<'t>,
162-
context: &mut Context,
163-
) -> ResolveResult<'t, 'py> {
164-
match self {
165-
Self::Text(text) => text.resolve(py, template, context),
166-
Self::TranslatedText(_text) => todo!(),
167-
Self::Variable(variable) => variable.resolve(py, template, context),
168-
Self::Filter(filter) => filter.resolve(py, template, context),
169-
Self::Int(int) => Ok(Some(Content::Int(int.clone()))),
170-
Self::Float(float) => Ok(Some(Content::Float(*float))),
171-
}
172-
}
173-
}
174-
175-
impl Render for TokenTree {
176-
fn render<'t>(
177-
&self,
178-
py: Python<'_>,
179-
template: TemplateString<'t>,
180-
context: &mut Context,
181-
) -> RenderResult<'t> {
182-
match self {
183-
Self::Text(text) => text.render(py, template, context),
184-
Self::TranslatedText(_text) => todo!(),
185-
Self::Tag(tag) => tag.render(py, template, context),
186-
Self::Variable(variable) => variable.render(py, template, context),
187-
Self::Filter(filter) => filter.render(py, template, context),
188-
}
189-
}
190-
}
191-
192-
#[cfg(test)]
193-
mod tests {
194-
use super::*;
195-
196-
use pyo3::types::{PyDict, PyList, PyString};
197-
198-
#[test]
199-
fn test_render_variable() {
200-
pyo3::prepare_freethreaded_python();
201-
202-
Python::with_gil(|py| {
203-
let name = PyString::new(py, "Lily").into_any();
204-
let context = HashMap::from([("name".to_string(), name.unbind())]);
205-
let mut context = Context {
206-
context,
207-
request: None,
208-
autoescape: false,
209-
};
210-
let template = TemplateString("{{ name }}");
211-
let variable = Variable::new((3, 4));
212-
213-
let rendered = variable.render(py, template, &mut context).unwrap();
214-
assert_eq!(rendered, "Lily");
215-
})
216-
}
217-
218-
#[test]
219-
fn test_render_dict_lookup() {
220-
pyo3::prepare_freethreaded_python();
221-
222-
Python::with_gil(|py| {
223-
let data = PyDict::new(py);
224-
let name = PyString::new(py, "Lily");
225-
data.set_item("name", name).unwrap();
226-
let context = HashMap::from([("data".to_string(), data.into_any().unbind())]);
227-
let mut context = Context {
228-
context,
229-
request: None,
230-
autoescape: false,
231-
};
232-
let template = TemplateString("{{ data.name }}");
233-
let variable = Variable::new((3, 9));
234-
235-
let rendered = variable.render(py, template, &mut context).unwrap();
236-
assert_eq!(rendered, "Lily");
237-
})
238-
}
239-
240-
#[test]
241-
fn test_render_list_lookup() {
242-
pyo3::prepare_freethreaded_python();
243-
244-
Python::with_gil(|py| {
245-
let name = PyString::new(py, "Lily");
246-
let names = PyList::new(py, [name]).unwrap();
247-
let context = HashMap::from([("names".to_string(), names.into_any().unbind())]);
248-
let mut context = Context {
249-
context,
250-
request: None,
251-
autoescape: false,
252-
};
253-
let template = TemplateString("{{ names.0 }}");
254-
let variable = Variable::new((3, 7));
255-
256-
let rendered = variable.render(py, template, &mut context).unwrap();
257-
assert_eq!(rendered, "Lily");
258-
})
259-
}
260-
261-
#[test]
262-
fn test_render_attribute_lookup() {
263-
pyo3::prepare_freethreaded_python();
264-
265-
Python::with_gil(|py| {
266-
let locals = PyDict::new(py);
267-
py.run(
268-
c"
269-
class User:
270-
def __init__(self, name):
271-
self.name = name
272-
273-
user = User('Lily')
274-
",
275-
None,
276-
Some(&locals),
277-
)
278-
.unwrap();
279-
280-
let context = locals.extract().unwrap();
281-
let mut context = Context {
282-
context,
283-
request: None,
284-
autoescape: false,
285-
};
286-
let template = TemplateString("{{ user.name }}");
287-
let variable = Variable::new((3, 9));
288-
289-
let rendered = variable.render(py, template, &mut context).unwrap();
290-
assert_eq!(rendered, "Lily");
291-
})
292-
}
293-
294-
#[test]
295-
fn test_render_html_autoescape() {
296-
pyo3::prepare_freethreaded_python();
297-
298-
Python::with_gil(|py| {
299-
let html = PyString::new(py, "<p>Hello World!</p>").into_any().unbind();
300-
let context = HashMap::from([("html".to_string(), html)]);
301-
let mut context = Context {
302-
context,
303-
request: None,
304-
autoescape: true,
305-
};
306-
let template = TemplateString("{{ html }}");
307-
let html = Variable::new((3, 4));
308-
309-
let rendered = html.render(py, template, &mut context).unwrap();
310-
assert_eq!(rendered, "&lt;p&gt;Hello World!&lt;/p&gt;");
311-
})
312-
}
313-
}

0 commit comments

Comments
 (0)