Skip to content
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

feat: add support for ignore_case for in string array #44

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Yggdrasil is a Rust project designed to create the core of the Unleash SDK domai

## Building the Core

Easy enough - run `cargo build` from the root of the project. You'll need an up to date set of Rust tools to do this.
Easy enough - run `cargo build` from the root of the project. You'll need an up to date set of Rust tools to do this (`rustup update stable`).
Copy link
Member

Choose a reason for hiding this comment

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

❤️


To run the client specs, you'll first need to clone them:

Expand Down Expand Up @@ -42,4 +42,5 @@ Start by setting up and activating a virtual environment in the python-sdk folde
python3 -m venv venv
source venv/bin/activate
```
Install [maturin](https://github.com/PyO3/maturin) by executing `pip install maturin` in your shell. Then you can run `maturin develop`.

Install [maturin](https://github.com/PyO3/maturin) by executing `pip install maturin` in your shell. Then you can run `maturin develop`.
2 changes: 2 additions & 0 deletions unleash-yggdrasil/src/strategy_grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ string_list_operation_without_case = {
starts_with_ignore_case
| ends_with_ignore_case
| contains_any_ignore_case
| equals_any_ignore_case
}
starts_with_ignore_case = { "starts_with_any_ignore_case" }
ends_with_ignore_case = { "ends_with_any_ignore_case" }
contains_any_ignore_case = { "contains_any_ignore_case" }
equals_any_ignore_case = { "equals_any_ignore_case" }

list_operation = { "in" | "not_in" }

Expand Down
60 changes: 49 additions & 11 deletions unleash-yggdrasil/src/strategy_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ enum StringComparator {
StartsWith,
EndsWith,
Contains,
Equals,
}

struct StringComparatorType {
Expand Down Expand Up @@ -171,6 +172,10 @@ fn to_string_comparator(node: Pair<Rule>) -> StringComparatorType {
ignore_case: true,
comparator_type: StringComparator::Contains,
},
"equals_any_ignore_case" => StringComparatorType {
ignore_case: true,
comparator_type: StringComparator::Equals,
},
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -407,6 +412,7 @@ fn string_fragment_constraint(inverted: bool, mut node: Pairs<Rule>) -> RuleFrag
StringComparator::Contains => list.iter().any(|item| value.contains(item)),
StringComparator::StartsWith => list.iter().any(|item| value.starts_with(item)),
StringComparator::EndsWith => list.iter().any(|item| value.ends_with(item)),
StringComparator::Equals => list.iter().any(|item| &value == item),
}
.invert(inverted)
} else {
Expand All @@ -418,7 +424,6 @@ fn string_fragment_constraint(inverted: bool, mut node: Pairs<Rule>) -> RuleFrag
fn constraint(mut node: Pairs<Rule>) -> RuleFragment {
let first = node.next();
let second = node.next();

let (inverted, child) = match (first, second) {
(Some(_), Some(second)) => (true, second),
(Some(first), None) => (false, first),
Expand Down Expand Up @@ -731,17 +736,50 @@ mod tests {
assert!(rule(&Context::default()));
}

#[test_case("user_id starts_with_any [\"some\"]", true)]
#[test_case("user_id ends_with_any [\".com\"]", true)]
#[test_case("user_id contains_any [\"email\"]", true)]
#[test_case("user_id contains_any [\"EMAIL\"]", false)]
#[test_case("user_id contains_any_ignore_case [\"EMAIL\"]", true)]
#[test_case("user_id ends_with_any_ignore_case [\".COM\"]", true)]
#[test_case("user_id starts_with_any_ignore_case [\"SOME\"]", true)]
fn run_string_operators_tests(rule: &str, expected: bool) {
#[test_case("some-email.com", "user_id starts_with_any [\"some\"]", true)]
#[test_case("some-email.com", "user_id ends_with_any [\".com\"]", true)]
#[test_case("some-email.com", "user_id contains_any [\"email\"]", true)]
#[test_case("some-email.com", "user_id contains_any [\"EMAIL\"]", false)]
#[test_case("some-email.com", "user_id contains_any_ignore_case [\"EMAIL\"]", true)]
#[test_case("some-email.com", "user_id ends_with_any_ignore_case [\".COM\"]", true)]
#[test_case(
"some-email.com",
"user_id starts_with_any_ignore_case [\"SOME\"]",
true
)]
#[test_case(
"some-email.com",
"user_id equals_any_ignore_case [\"some-EMAIL.com\"]",
true
)]
#[test_case(
"some-email.com",
"user_id equals_any_ignore_case [\"noemail.com\",\"neither-THIS.com\"]",
false
)]
#[test_case(
"some-email.com",
"! user_id equals_any_ignore_case [\"notemail.com\"]",
true
)]
#[test_case(
"some-email.com",
"! user_id equals_any_ignore_case [\"notemail.com\",\"some-EMAIL.com\"]",
false
)]
gastonfournier marked this conversation as resolved.
Show resolved Hide resolved
#[test_case(
"sOMeUSer-email.com",
"! user_id equals_any_ignore_case [\"someuser\"]",
true
)]
#[test_case(
"sOMeUSer-email.com",
"user_id equals_any_ignore_case [\"someuser-EMAIL.com\"]",
true
)]
fn run_string_operators_tests(user_id: &str, rule: &str, expected: bool) {
let rule = compile_rule(rule).expect("");
let context = context_from_user_id("some-email.com");

let context = context_from_user_id(user_id);
assert_eq!(rule(&context), expected);
}

Expand Down