Skip to content

Feat/yesno filter #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

oliverhaas
Copy link

Hello!

I got really interested in the project a few weeks back, and I thought I just have a go at contributing.

I honestly have no experience in rust, so if it gets to a point where my contributions are more a net negative, just let me know.

I don't really understand most parts of the project, and I even have some basic questions. Maybe if I pick one for now: If I'm going to implement several of the filters, do we really need the rust tests in addition to the python tests? The filter usually don't have any fancy logic, and then I personally would be fine with just testing the "blackbox" behavior with the python tests.

Copy link

codecov bot commented Apr 19, 2025

Codecov Report

Attention: Patch coverage is 91.41104% with 14 lines in your changes missing coverage. Please review.

Project coverage is 98.37%. Comparing base (a7e3e9f) to head (caa2a80).

Files with missing lines Patch % Lines
src/render/filters.rs 87.38% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #78      +/-   ##
==========================================
- Coverage   98.56%   98.37%   -0.20%     
==========================================
  Files          43       44       +1     
  Lines        5853     6016     +163     
  Branches     1913     1965      +52     
==========================================
+ Hits         5769     5918     +149     
- Misses         63       77      +14     
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LilyAcorn
Copy link
Owner

LilyAcorn commented Apr 19, 2025

Hello!

Hi!

I got really interested in the project a few weeks back, and I thought I just have a go at contributing.

Awesome!

I honestly have no experience in rust, so if it gets to a point where my contributions are more a net negative, just let me know.

I'm sure that won't be a problem.

I don't really understand most parts of the project, and I even have some basic questions.

I'm happy to try and answer any questions you have. 😄

Maybe if I pick one for now: If I'm going to implement several of the filters, do we really need the rust tests in addition to the python tests? The filter usually don't have any fancy logic, and then I personally would be fine with just testing the "blackbox" behavior with the python tests.

For now, I don't really mind whether the tests are written in Rust or in Python, so long as we have (close to) 100% coverage in CI.

Copy link
Owner

@LilyAcorn LilyAcorn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an awesome start!

I've got a bunch of suggestions for improvement. Please ask if you want more explanation for anything.

Comment on lines +335 to +340
None => return Err(PyRenderError::RenderError(RenderError::VariableDoesNotExist {
key: "yesno".to_string(),
object: "filter".to_string(),
key_at: (0, 0).into(),
object_at: None,
})),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test this code path?

You can find an example of the sort of way I'd test errors here:
https://github.com/LilyFoote/django-rusty-templates/blob/a7e3e9fc524ea65a5fc9c91eeb52ce7f6c815f07/tests/filters/test_add.py#L25-L45

Comment on lines +348 to +358
let mapping = match arg_content.to_py(py)?.extract::<String>() {
Ok(s) => s,
Err(_) => {
return Err(PyRenderError::RenderError(RenderError::VariableDoesNotExist {
key: "yesno argument".to_string(),
object: "string".to_string(),
key_at: (0, 0).into(),
object_at: None,
}));
}
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't need to call to_py here. Can we instead use resolve_string?

https://github.com/LilyFoote/django-rusty-templates/blob/a7e3e9fc524ea65a5fc9c91eeb52ce7f6c815f07/src/render/types.rs#L86

Something like:

Suggested change
let mapping = match arg_content.to_py(py)?.extract::<String>() {
Ok(s) => s,
Err(_) => {
return Err(PyRenderError::RenderError(RenderError::VariableDoesNotExist {
key: "yesno argument".to_string(),
object: "string".to_string(),
key_at: (0, 0).into(),
object_at: None,
}));
}
};
let mapping = arg_content.resolve_string(context)?.content();

Otherwise, we should probably match arg_content and handle each case directly.


def test_yesno_two_options(self):
# Test with only two options - None uses the second option
template = Template("{{ var|yesno:'yep,nah' }}")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have tests for three and for two mappings. We should also test that we match Django's behaviour for zero, one or more than three mappings.

Comment on lines +365 to +370
let result = if parts.len() >= 3 {
parts[2].to_string()
} else {
parts.get(1).unwrap_or(&"no").to_string()
};
return Ok(Some(Content::String(Cow::Owned(result))));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be cleaner to match on the length of parts and handle each case explicitly:

Suggested change
let result = if parts.len() >= 3 {
parts[2].to_string()
} else {
parts.get(1).unwrap_or(&"no").to_string()
};
return Ok(Some(Content::String(Cow::Owned(result))));
let result = match parts.len() {
3 => Cow::Owned(parts[2].to_string()),
2 => Cow::Owned(parts[1].to_string()),
1 => Cow::Borrowed("no"),
0 => todo!("Match Django's behaviour"),
_ => todo!("Match Django's behaviour"),
};
return Ok(Some(Content::String(result)));

let parts: Vec<&str> = mapping.split(',').collect();

// Handle None values
if left.to_py(py)?.is_none() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should add a new method to Content returning an enum with three variants: None, Truthy and Falsey, which we can then match on here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants