use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET};
use anyhow::Result;
use log::{debug, error};
use crate::client::Client;
use crate::entities::{CreateIssue, Issue, IssueState, Project, ProjectMilestone};
impl Issue {
pub fn create(client: &Client, issue: &CreateIssue) -> Result<Issue> {
let api_path = {
let project_name_or_id =
utf8_percent_encode(&issue.project_id, PATH_SEGMENT_ENCODE_SET).to_string();
format!("api/v4/projects/{}/issues", project_name_or_id)
};
let response = client.post_json(&api_path, &issue)?;
let de = &mut serde_json::Deserializer::from_reader(response);
let result: Result<Issue, _> = serde_path_to_error::deserialize(de);
match result {
Ok(issue) => Ok(issue),
Err(err) => {
dbg!(&api_path);
dbg!(err.path().to_string());
dbg!(client.get(&api_path)?.text()?);
Err(err.into_inner().into())
}
}
}
pub fn issue_from_branch(
client: &Client,
project: &str,
branch: &str,
) -> Option<Result<Issue>> {
let prefix = branch.split('-').next();
let issue_iid = prefix.and_then(|iid| iid.parse::<usize>().ok());
issue_iid.map(|issue_iid| Issue::single(&client, &project, issue_iid))
}
pub fn single(client: &Client, project: &str, issue_iid: usize) -> Result<Issue> {
let api_path = {
let project_name_or_id =
utf8_percent_encode(project, PATH_SEGMENT_ENCODE_SET).to_string();
format!(
"api/v4/projects/{}/issues/{}",
project_name_or_id, issue_iid
)
};
let response = client.get(&api_path)?;
let de = &mut serde_json::Deserializer::from_reader(response);
let result: Result<Issue, _> = serde_path_to_error::deserialize(de);
match result {
Ok(issue) => Ok(issue),
Err(err) => {
error!("Path: {}", err.path().to_string());
error!("Body: {:?}", client.get(&api_path)?.bytes()?);
Err(err.into_inner().into())
}
}
}
pub fn list(
client: &Client,
project: &Option<String>,
state: &IssueState,
labels: &Option<String>,
milestone: &Option<String>,
limit: usize,
) -> Result<Vec<Issue>> {
let prefix = if let Some(aproject) = project {
let project_name_or_id =
utf8_percent_encode(aproject, PATH_SEGMENT_ENCODE_SET).to_string();
format!("api/v4/projects/{}", project_name_or_id)
} else {
"api/v4".to_string()
};
let scope = {
if project.is_some() {
"all"
} else {
"assigned_to_me" }
};
let mut api_path = format!("{}/issues?state={}&scope={}", prefix, state.name(), scope);
if let Some(labels) = labels {
api_path = format!("{}&labels={}", api_path, labels);
}
if let Some(milestone) = milestone {
api_path = format!("{}&milestone={}", api_path, milestone);
}
debug!("Ready to query {:?}", api_path);
let mut all_issues: Vec<Issue> = vec![];
for issues in client.get_paginated(&api_path) {
let issues = issues?;
let de = &mut serde_json::Deserializer::from_reader(issues);
let result: Result<Vec<Issue>, _> = serde_path_to_error::deserialize(de);
let issues = match result {
Ok(issues) => issues,
Err(err) => {
dbg!(&api_path);
dbg!(err.path().to_string());
dbg!(client.get(&api_path)?.text()?);
return Err(err.into_inner().into());
}
};
all_issues.extend(issues);
if all_issues.len() >= limit {
break;
}
}
Ok(all_issues)
}
}
impl ProjectMilestone {
pub fn in_project_by_title(
client: &Client,
project: &str,
title: &str,
) -> Result<Option<ProjectMilestone>> {
ProjectMilestone::_in_project_by_title(client, project, Some(title)).map(|mut m| m.pop())
}
pub fn in_project(client: &Client, project: &str) -> Result<Vec<ProjectMilestone>> {
ProjectMilestone::_in_project_by_title(client, project, None)
}
fn _in_project_by_title(
client: &Client,
project: &str,
title: Option<&str>,
) -> Result<Vec<ProjectMilestone>> {
let prefix = {
let project_name_or_id =
utf8_percent_encode(project, PATH_SEGMENT_ENCODE_SET).to_string();
format!("api/v4/projects/{}", project_name_or_id)
};
let mut api_path = format!(
"{}/milestones?state=active&include_parent_milestones=true",
prefix
);
if let Some(title) = title {
api_path = format!("{}&title={}", api_path, title);
}
debug!("Ready to query {:?}", api_path);
let response = client.get(&api_path)?;
let de = &mut serde_json::Deserializer::from_reader(response);
let result: Result<Vec<ProjectMilestone>, _> = serde_path_to_error::deserialize(de);
let milestones = match result {
Ok(milestones) => milestones,
Err(err) => {
dbg!(&api_path);
dbg!(err.path().to_string());
dbg!(client.get(&api_path)?.text()?);
return Err(err.into_inner().into());
}
};
Ok(milestones)
}
}
impl Project {
pub fn list(client: &Client, search: &Option<String>, limit: usize) -> Result<Vec<Project>> {
let mut api_path = "api/v4/projects?simple=true&oder_by=id".to_string();
if let Some(search) = search {
api_path = format!("{}&search={}", api_path, search);
}
debug!("Ready to query {:?}", api_path);
let mut all_projects = vec![];
for projects in client.get_paginated(&api_path) {
let response = projects?;
let de = &mut serde_json::Deserializer::from_reader(response);
let result: Result<Vec<Project>, _> = serde_path_to_error::deserialize(de);
let projects = match result {
Ok(projects) => projects,
Err(err) => {
dbg!(&api_path);
dbg!(err.path().to_string());
return Err(err.into_inner().into());
}
};
all_projects.extend(projects);
if all_projects.len() >= limit {
break;
}
}
Ok(all_projects)
}
pub fn single(client: &Client, name_or_id: &str) -> Result<Project> {
let api_path = {
let name_or_id = utf8_percent_encode(name_or_id, PATH_SEGMENT_ENCODE_SET).to_string();
format!("api/v4/projects/{}", name_or_id)
};
let response = client.get(&api_path)?;
let de = &mut serde_json::Deserializer::from_reader(response);
let result: Result<Project, _> = serde_path_to_error::deserialize(de);
match result {
Ok(project) => Ok(project),
Err(err) => {
dbg!(&api_path);
dbg!(err.path().to_string());
dbg!(client.get(&api_path)?.text()?);
Err(err.into_inner().into())
}
}
}
}