Skip to content

Commit

Permalink
feat(#27): all cases, puzzles
Browse files Browse the repository at this point in the history
  • Loading branch information
h1alexbel committed Aug 1, 2024
1 parent 1dcc334 commit 5f24fca
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 81 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ We support the following issue scope dimensions: `author`, `label`, and

* assignment: `author:jeff`
* exclusion: `label:!bug`
* multiple values: `author:[jeff,foo,max]` (no space in between!)
* multiple values to exclude: `author:![jeff,foo]`
* multiple values with `author` and `label`: `author:[jeff,foo,max]`
* multiple values to exclude with `author` and `label`: `author:![jeff,foo]`
* "starts with" with `title`: `title:*this is feature request:`
* "starts with" exclusion with `title`: `title:!*[BUG]`, thus it will skip
everything that not matches `[BUG]...`.

### Using with GitHub Actions

Expand Down
214 changes: 136 additions & 78 deletions src/args/ignore_facts.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
// The MIT License (MIT)
//
// Copyright (c) 2024 Aliaksei Bialiauski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use std::collections::HashMap;

/// Parse facts from list of strings (lines).
// @todo #27:30min Read ignore.ghiqc file as list of strings.
// We should read file first in the main.rs, and then pass list of strings in
// parse_facts function.
pub fn parse_facts(content: Vec<String>) -> HashMap<String, Vec<String>> {
let mut facts: HashMap<String, Vec<String>> = HashMap::new();
let mut author = vec![];
Expand All @@ -22,68 +47,62 @@ pub fn parse_facts(content: Vec<String>) -> HashMap<String, Vec<String>> {
facts
}

pub fn ignores(issue: String, facts: HashMap<String, Vec<String>>) -> bool {
let title = facts.get("title").expect("Failed to obtain title facts");
// let collected = title.iter()
// .filter(
// |f|
// if f.starts_with("!") {
// return if f[1..].starts_with("*") {
// !issue.title.starts_with(f.clone())
// } else {
// !issue.title.eq(f.clone())
// };
// } else {
// if f.starts_with("*") {
// issue.title.starts_with(f.clone())
// } else {
// issue.title.eq(f.clone())
// }
// }
// )
// .collect();
let matches_title = title.iter().any(|f| {
if f.starts_with("!") {
if f[1..].starts_with("*") {
!issue.starts_with(&f[1..])
} else {
!issue.eq(f)
}
} else {
if f.starts_with("*") {
issue.starts_with(&f[1..])
/// Ignore title?
// @todo #27:35min Implement ignores_author.
// Let's do this similarly to ignores_title function. Check
// README.md#ignore-issues-from-checking for more information about the rules.
// Don't forget to remove this puzzle.
// @todo #27:35min Implement ignores_label.
// Let's do this similarly to ignores_title function. Check
// README.md#ignore-issues-from-checking for more information about the rules.
// Don't forget to remove this puzzle.
pub fn ignores_title(
issue: String,
facts: HashMap<String, Vec<String>>,
) -> bool {
facts
.get("title")
.expect("Failed to obtain title facts")
.iter()
.any(|f| {
if f.starts_with('!') {
if f[1..].starts_with('*') {
!issue.starts_with(&f[2..])
} else {
!issue.eq(&f[1..])
}
} else {
issue.eq(f)
if f.starts_with('*') {
issue.starts_with(&f[1..])
} else {
issue.eq(f)
}
}
}
});
matches_title
})
}

#[cfg(test)]
mod tests {
use anyhow::Result;
use hamcrest::{equal_to, HamcrestMatcher, is};
use hamcrest::{equal_to, is, HamcrestMatcher};

use crate::args::ignore_facts::{ignores, parse_facts};
use crate::args::ignore_facts::{ignores_title, parse_facts};

#[allow(clippy::unwrap_used)]
#[test]
fn parses_facts_in_map() -> Result<()> {
let facts = parse_facts(
vec![
String::from("author:jeff"),
String::from("label:enhancement"),
String::from("title:new feature request"),
String::from("title:!*something"),
]
let facts = parse_facts(vec![
String::from("author:jeff"),
String::from("label:enhancement"),
String::from("title:new feature request"),
String::from("title:!*something"),
]);
let author = vec![String::from("jeff")];
assert_that!(
facts.get("author").unwrap().to_vec(),
is(equal_to(author))
);
let author = vec![
String::from("jeff")
];
assert_that!(facts.get("author").unwrap().to_vec(), is(equal_to(author)));
let label = vec![
String::from("enhancement")
];
let label = vec![String::from("enhancement")];
assert_that!(facts.get("label").unwrap().to_vec(), is(equal_to(label)));
let title = vec![
String::from("new feature request"),
Expand All @@ -94,57 +113,96 @@ mod tests {
}

#[test]
#[should_panic(expected = "Parsing error in line invalid:something: unsupported syntax")]
#[should_panic(
expected = "Parsing error in line invalid:something: unsupported syntax"
)]
fn panics_on_supported_syntax() {
parse_facts(
vec![
String::from("author:jeff"),
String::from("invalid:something"),
]
);
parse_facts(vec![
String::from("author:jeff"),
String::from("invalid:something"),
]);
}

#[test]
fn ignores_on_title_exact_match() -> Result<()> {
let ignore = ignores(
let ignore = ignores_title(
String::from("new feature request"),
parse_facts(
vec![
String::from("title:new feature request"),
String::from("title:!*something"),
]
),
parse_facts(vec![String::from("title:new feature request")]),
);
assert_that!(ignore, is(equal_to(true)));
Ok(())
}

#[test]
pub fn ignores_on_title_star_match() -> Result<()> {
let ignore = ignores(
let ignore = ignores_title(
String::from("new feature request: ABC"),
parse_facts(
vec![
String::from("title:*new feature request")
]
),
parse_facts(vec![String::from("title:*new feature request")]),
);
assert_that!(ignore, is(equal_to(true)));
Ok(())
}

#[test]
pub fn ignores_on_partial_facts_match() -> Result<()> {
let ignore = ignores(
let ignore = ignores_title(
String::from("something!"),
parse_facts(
vec![
String::from("title:*something"),
String::from("title:different")
]
)
parse_facts(vec![
String::from("title:*something"),
String::from("title:different"),
]),
);
assert_that!(ignore, is(equal_to(true)));
Ok(())
}

#[test]
pub fn does_not_ignore_when_unmatch() -> Result<()> {
let ignore = ignores_title(
String::from("different"),
parse_facts(vec![String::from("title:exact")]),
);
assert_that!(ignore, is(equal_to(false)));
Ok(())
}

#[test]
pub fn does_not_ignore_on_star_unmatch() -> Result<()> {
let ignore = ignores_title(
String::from("different"),
parse_facts(vec![String::from("title:*something")]),
);
assert_that!(ignore, is(equal_to(false)));
Ok(())
}

#[test]
pub fn ignores_on_inverse_match() -> Result<()> {
let ignore = ignores_title(
String::from("i'm good one!"),
parse_facts(vec![String::from("title:!bad")]),
);
assert_that!(ignore, is(equal_to(true)));
Ok(())
}

#[test]
pub fn does_not_ignore_on_inverse_unmatch() -> Result<()> {
let ignore = ignores_title(
String::from("bad"),
parse_facts(vec![String::from("title:!bad")]),
);
assert_that!(ignore, is(equal_to(false)));
Ok(())
}

#[test]
pub fn ignores_on_inverse_star_match() -> Result<()> {
let ignore = ignores_title(
String::from("good one"),
parse_facts(vec![String::from("title:!*good")]),
);
assert_that!(ignore, is(equal_to(false)));
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@
pub mod cli;
/// Environment variables.
pub mod env;
/// Ignore issue based on facts.
pub mod ignore_facts;
4 changes: 3 additions & 1 deletion tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ fn outputs_help() -> Result<()> {
let assertion = Command::cargo_bin("ghiqc")?.arg("--help").assert();
let bytes = assertion.get_output().stdout.as_slice();
let output = str::from_utf8(bytes)?;
assert!(output.contains("Usage: ghiqc [OPTIONS] --repo <REPO> --issue <ISSUE>"));
assert!(
output.contains("Usage: ghiqc [OPTIONS] --repo <REPO> --issue <ISSUE>")
);
assert!(output.contains("--help"));
assert!(output.contains("Print help"));
assert!(output.contains("--repo"));
Expand Down

0 comments on commit 5f24fca

Please sign in to comment.