pub mod error;
use url::Url;
use self::error::{Error, Result};
#[derive(Debug, PartialEq, Eq)]
pub struct Issue<'a> {
repository_name: &'a str,
repository_owner: &'a str,
params: Vec<(&'a str, &'a str)>,
}
impl<'a> Issue<'a> {
pub fn new(repository_name: &'a str, repository_owner: &'a str) -> Result<Self> {
if repository_name.is_empty() {
return Err(Error::EmptyRepositoryName);
}
if repository_owner.is_empty() {
return Err(Error::EmptyRepositoryOwner);
}
Ok(Issue {
repository_name,
repository_owner,
params: Vec::new(),
})
}
pub fn assignee(&mut self, assignee: &'a str) {
self.params.push(("assignee", assignee));
}
pub fn body(&mut self, body: &'a str) {
self.params.push(("body", body));
}
pub fn labels(&mut self, labels: &'a str) {
self.params.push(("labels", labels));
}
pub fn milestone(&mut self, milestone: &'a str) {
self.params.push(("milestone", milestone));
}
pub fn projects(&mut self, projects: &'a str) {
self.params.push(("projects", projects));
}
pub fn title(&mut self, title: &'a str) {
self.params.push(("title", title));
}
pub fn template(&mut self, template: &'a str) {
self.params.push(("template", template));
}
pub fn url(&'a self) -> Result<String> {
let repository_url = format!(
"https://github.com/{}/{}/issues/new",
self.repository_owner, self.repository_name
);
let url = Url::parse_with_params(repository_url.as_str(), self.params.iter())
.map_err(|e| Error::UrlParseError(e.to_string()))?;
Ok(url.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
const GITHUB_ISSUE_LINK: &str = "https://github.com/EstebanBorai/github-issue-url/issues/new?title=Null%3A+The+Billion+Dollar+Mistake&body=Null+is+a+flag.+It+represents+different+situations+depending+on+the+context+in+which+it+is+used+and+invoked.+This+yields+the+most+serious+error+in+software+development%3A+Coupling+a+hidden+decision+in+the+contract+between+an+object+and+who+uses+it.&template=bug_report.md&labels=bug%2Cproduction%2Chigh-severity&assignee=EstebanBorai&milestone=1&projects=1";
const SAMPLE_ISSUE_BODY: &str = r#"Null is a flag. It represents different situations depending on the context in which it is used and invoked. This yields the most serious error in software development: Coupling a hidden decision in the contract between an object and who uses it."#;
#[test]
fn build_issue_url() {
let mut have = Issue::new("github-issue-url", "EstebanBorai").unwrap();
have.title("Null: The Billion Dollar Mistake");
have.body(SAMPLE_ISSUE_BODY);
have.template("bug_report.md");
have.labels("bug,production,high-severity");
have.assignee("EstebanBorai");
have.milestone("1");
have.projects("1");
let have = have.url().unwrap();
assert_eq!(have, GITHUB_ISSUE_LINK.to_string());
}
#[test]
fn return_error_if_repository_owner_is_invalid() {
let have = Issue::new("github-issue-url", "");
assert!(have.is_err());
assert!(matches!(have, Err(Error::EmptyRepositoryOwner)));
assert_eq!(
String::from("Repository owner name is not defined"),
have.err().unwrap().to_string()
);
}
#[test]
fn return_error_if_repository_name_is_invalid() {
let have = Issue::new("", "EstebanBorai");
assert!(have.is_err());
assert!(matches!(have, Err(Error::EmptyRepositoryName)));
assert_eq!(
String::from("Repository name is not defined"),
have.err().unwrap().to_string()
);
}
}