diff --git a/src/render/tags.rs b/src/render/tags.rs index 195b64e..492dcf5 100644 --- a/src/render/tags.rs +++ b/src/render/tags.rs @@ -447,25 +447,18 @@ impl Contains for Content<'_, '_> { } #[derive(Debug)] -enum LeftResolved<'t, 'py> { +enum Resolved<'t, 'py> { Content(Option>), Evaluate(bool), } -#[derive(Debug)] -enum RightResolved<'t, 'py> { - Content(Option>), - Evaluate(bool), - None, -} - trait ResolveTuple<'t, 'py> { fn resolve( &self, py: Python<'py>, template: TemplateString<'t>, context: &mut Context, - ) -> Result<(LeftResolved<'t, 'py>, RightResolved<'t, 'py>), PyRenderError>; + ) -> Result<(Resolved<'t, 'py>, Resolved<'t, 'py>), PyRenderError>; } impl<'t, 'py> ResolveTuple<'t, 'py> for (IfCondition, IfCondition) { @@ -474,38 +467,36 @@ impl<'t, 'py> ResolveTuple<'t, 'py> for (IfCondition, IfCondition) { py: Python<'py>, template: TemplateString<'t>, context: &mut Context, - ) -> Result<(LeftResolved<'t, 'py>, RightResolved<'t, 'py>), PyRenderError> { + ) -> Result<(Resolved<'t, 'py>, Resolved<'t, 'py>), PyRenderError> { const IGNORE: ResolveFailures = ResolveFailures::IgnoreVariableDoesNotExist; Ok(match self { (IfCondition::Variable(l), IfCondition::Variable(r)) => { let left = l.resolve(py, template, context, IGNORE)?; let right = r.resolve(py, template, context, IGNORE)?; - (LeftResolved::Content(left), RightResolved::Content(right)) + (Resolved::Content(left), Resolved::Content(right)) } (IfCondition::Variable(l), r) => { let left = l.resolve(py, template, context, IGNORE)?; - let right = r.evaluate(py, template, context); - match right { - Some(right) => (LeftResolved::Content(left), RightResolved::Evaluate(right)), - None => (LeftResolved::Content(left), RightResolved::None), - } + let right = r + .evaluate(py, template, context) + .expect("Right cannot be an expression that evaluates to None"); + (Resolved::Content(left), Resolved::Evaluate(right)) } (l, IfCondition::Variable(r)) => { let left = l .evaluate(py, template, context) .expect("Left cannot be an expression that evaluates to None"); let right = r.resolve(py, template, context, IGNORE)?; - (LeftResolved::Evaluate(left), RightResolved::Content(right)) + (Resolved::Evaluate(left), Resolved::Content(right)) } (l, r) => { let left = l .evaluate(py, template, context) .expect("Left cannot be an expression that evaluates to None"); - let right = r.evaluate(py, template, context); - match right { - Some(right) => (LeftResolved::Evaluate(left), RightResolved::Evaluate(right)), - None => (LeftResolved::Evaluate(left), RightResolved::None), - } + let right = r + .evaluate(py, template, context) + .expect("Right cannot be an expression that evaluates to None"); + (Resolved::Evaluate(left), Resolved::Evaluate(right)) } }) } @@ -528,21 +519,32 @@ impl Evaluate for IfCondition { Self::Or(inner) => { let left = inner.0.evaluate(py, template, context); let right = inner.1.evaluate(py, template, context); - if left? { true } else { right.unwrap_or(false) } + match left { + None => false, + Some(left) => { + if left { + true + } else { + right.unwrap_or(false) + } + } + } } - Self::Not(inner) => !inner.evaluate(py, template, context)?, + Self::Not(inner) => match inner.evaluate(py, template, context) { + None => false, + Some(true) => false, + Some(false) => true, + }, Self::Equal(inner) => { let inner = match inner.resolve(py, template, context) { Ok(inner) => inner, Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.eq(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.eq(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.eq(&r), - (LeftResolved::Content(l), RightResolved::None) => l.eq(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l.eq(&r), - (LeftResolved::Evaluate(l), RightResolved::None) => !l, + (Resolved::Content(l), Resolved::Content(r)) => l.eq(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.eq(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.eq(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l.eq(&r), } } Self::NotEqual(inner) => { @@ -551,12 +553,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.ne(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.ne(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.ne(&r), - (LeftResolved::Content(l), RightResolved::None) => l.ne(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l.ne(&r), - (LeftResolved::Evaluate(l), RightResolved::None) => l, + (Resolved::Content(l), Resolved::Content(r)) => l.ne(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.ne(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.ne(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l.ne(&r), } } Self::LessThan(inner) => { @@ -565,12 +565,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.lt(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.gt(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.lt(&r), - (LeftResolved::Content(l), RightResolved::None) => l.lt(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l < r, - (LeftResolved::Evaluate(_), RightResolved::None) => false, + (Resolved::Content(l), Resolved::Content(r)) => l.lt(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.gt(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.lt(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l < r, } } Self::GreaterThan(inner) => { @@ -579,12 +577,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.gt(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.lt(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.gt(&r), - (LeftResolved::Content(l), RightResolved::None) => l.gt(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l > r, - (LeftResolved::Evaluate(l), RightResolved::None) => l, + (Resolved::Content(l), Resolved::Content(r)) => l.gt(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.lt(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.gt(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l > r, } } Self::LessThanEqual(inner) => { @@ -593,12 +589,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.lte(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.gte(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.lte(&r), - (LeftResolved::Content(l), RightResolved::None) => l.lte(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l <= r, - (LeftResolved::Evaluate(l), RightResolved::None) => !l, + (Resolved::Content(l), Resolved::Content(r)) => l.lte(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.gte(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.lte(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l <= r, } } Self::GreaterThanEqual(inner) => { @@ -607,12 +601,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => l.gte(&r), - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => r.lte(&l), - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => l.gte(&r), - (LeftResolved::Content(l), RightResolved::None) => l.gte(&false), - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l >= r, - (LeftResolved::Evaluate(_), RightResolved::None) => true, + (Resolved::Content(l), Resolved::Content(r)) => l.gte(&r), + (Resolved::Evaluate(l), Resolved::Content(r)) => r.lte(&l), + (Resolved::Content(l), Resolved::Evaluate(r)) => l.gte(&r), + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l >= r, } } Self::In(inner) => { @@ -621,10 +613,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(Some(r))) => { + (Resolved::Content(l), Resolved::Content(Some(r))) => { r.contains(l).unwrap_or(false) } - (LeftResolved::Evaluate(l), RightResolved::Content(Some(r))) => { + (Resolved::Evaluate(l), Resolved::Content(Some(r))) => { r.contains(l).unwrap_or(false) } _ => false, @@ -636,10 +628,10 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(Some(r))) => { + (Resolved::Content(l), Resolved::Content(Some(r))) => { !(r.contains(l).unwrap_or(true)) } - (LeftResolved::Evaluate(l), RightResolved::Content(Some(r))) => { + (Resolved::Evaluate(l), Resolved::Content(Some(r))) => { !(r.contains(l).unwrap_or(true)) } _ => false, @@ -651,7 +643,7 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => match (l, r) { + (Resolved::Content(l), Resolved::Content(r)) => match (l, r) { (Some(Content::Py(left)), Some(Content::Py(right))) => left.is(&right), (Some(Content::Py(obj)), None) | (None, Some(Content::Py(obj))) => { obj.is(PyNone::get(py).as_any()) @@ -659,7 +651,7 @@ impl Evaluate for IfCondition { (None, None) => true, _ => false, }, - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => match r { + (Resolved::Evaluate(l), Resolved::Content(r)) => match r { None => false, Some(Content::Py(right)) => right.is(PyBool::new(py, l).as_any()), _ => false, @@ -673,7 +665,7 @@ impl Evaluate for IfCondition { Err(_) => return Some(false), }; match inner { - (LeftResolved::Content(l), RightResolved::Content(r)) => match (l, r) { + (Resolved::Content(l), Resolved::Content(r)) => match (l, r) { (Some(Content::Py(left)), Some(Content::Py(right))) => !left.is(&right), (Some(Content::Py(obj)), None) | (None, Some(Content::Py(obj))) => { !obj.is(PyNone::get(py).as_any()) @@ -681,16 +673,15 @@ impl Evaluate for IfCondition { (None, None) => false, _ => true, }, - (LeftResolved::Evaluate(l), RightResolved::Content(r)) => match r { + (Resolved::Evaluate(l), Resolved::Content(r)) => match r { Some(Content::Py(right)) => !right.is(PyBool::new(py, l).as_any()), _ => true, }, - (LeftResolved::Content(l), RightResolved::Evaluate(r)) => match l { + (Resolved::Content(l), Resolved::Evaluate(r)) => match l { Some(Content::Py(left)) => !left.is(PyBool::new(py, r).as_any()), _ => true, }, - (LeftResolved::Evaluate(l), RightResolved::Evaluate(r)) => l != r, - (_, RightResolved::None) => true, + (Resolved::Evaluate(l), Resolved::Evaluate(r)) => l != r, } } }) diff --git a/tests/tags/test_if.py b/tests/tags/test_if.py index 660f437..1be6db3 100644 --- a/tests/tags/test_if.py +++ b/tests/tags/test_if.py @@ -1005,7 +1005,9 @@ def test_render_none_or_default_or_1(): def test_render_default_and_none_or_not_none(): - template = "{% if A|default:inf and None or not None %}truthy{% else %}falsey{% endif %}" + template = ( + "{% if A|default:inf and None or not None %}truthy{% else %}falsey{% endif %}" + ) django_template = engines["django"].from_string(template) rust_template = engines["rusty"].from_string(template) @@ -1068,7 +1070,9 @@ def test_op_not(a, op): @pytest.mark.parametrize("op", ["==", "!="]) def test_comparison_op_not(op): expected = "truthy" if compare(op, True, False) else "falsey" - template = f"{{% if 'foo' == 'foo' {op} not 'bar' %}}truthy{{% else %}}falsey{{% endif %}}" + template = ( + f"{{% if 'foo' == 'foo' {op} not 'bar' %}}truthy{{% else %}}falsey{{% endif %}}" + ) django_template = engines["django"].from_string(template) rust_template = engines["rusty"].from_string(template) @@ -1092,7 +1096,9 @@ def test_missing_op_other(op): def test_number_op_boolean(number, op, boolean): expected = "truthy" if compare(op, number, boolean) else "falsey" boolean = not boolean - template = f"{{% if {number} {op} not {boolean} %}}truthy{{% else %}}falsey{{% endif %}}" + template = ( + f"{{% if {number} {op} not {boolean} %}}truthy{{% else %}}falsey{{% endif %}}" + ) django_template = engines["django"].from_string(template) rust_template = engines["rusty"].from_string(template) @@ -1266,7 +1272,18 @@ def test_bool_is_not_expr(): def test_string_is_not_missing(): - template = "{% if 'foo' is not not bar|default:bar %}truthy{% else %}falsey{% endif %}" + template = ( + "{% if 'foo' is not not bar|default:bar %}truthy{% else %}falsey{% endif %}" + ) + django_template = engines["django"].from_string(template) + rust_template = engines["rusty"].from_string(template) + + assert django_template.render({}) == "truthy" + assert rust_template.render({}) == "truthy" + + +def test_not_inf_or_not_none(): + template = "{% if not A|default:inf or not None %}truthy{% else %}falsey{% endif %}" django_template = engines["django"].from_string(template) rust_template = engines["rusty"].from_string(template) diff --git a/tests/test_engine.py b/tests/test_engine.py index 6eced50..3b3ab47 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -44,12 +44,14 @@ def test_import_libraries_no_register(): def test_pathlib_dirs(): - engine = RustyTemplates({ - "NAME": "rust", - "OPTIONS": {}, - "DIRS": [Path(settings.BASE_DIR) / "templates"], - "APP_DIRS": False, - }) + engine = RustyTemplates( + { + "NAME": "rust", + "OPTIONS": {}, + "DIRS": [Path(settings.BASE_DIR) / "templates"], + "APP_DIRS": False, + } + ) template = engine.get_template("basic.txt") assert template.render({"user": "Lily"}) == "Hello Lily!\n"