use call;
use config;
use error::GGRError;
use error::GGRResult;
use error::GerritError;
use entities;
use serde;
use std;
use url;
const ENDPOINT: &'static str = "/a/changes";
pub struct Changes {
call: call::Call,
}
impl<'de> Changes {
pub fn new(url: &url::Url) -> Changes {
Changes {
call: call::Call::new(url),
}
}
fn build_query_string<S>(querylist: Option<Vec<S>>) -> String
where S: Into<String> {
let mut querystring = String::new();
if let Some(querylist) = querylist {
if ! querylist.is_empty() {
querystring.push_str("&q=");
let mut fragment = String::new();
for el in querylist {
fragment.push_str(&el.into()[..]);
fragment.push_str("+");
}
if let Some(x) = fragment.chars().last() {
if x == '+' {
fragment = fragment.trim_right_matches(x).to_string();
}
};
querystring = format!("{}{}", querystring, fragment);
};
};
debug!("query-string: '{}'", querystring);
querystring
}
fn build_label_string<S>(labellist: Option<Vec<S>>) -> String
where S: Into<String> {
let mut labelstring = String::new();
if let Some(labellist) = labellist {
if ! labellist.is_empty() {
for label in labellist {
if labelstring.is_empty() {
labelstring = format!("o={}", label.into());
} else {
labelstring = format!("{}&o={}", labelstring, label.into());
}
}
}
};
debug!("label-string: '{}'", labelstring);
labelstring
}
fn execute<INPUT,OUTPUT>(c: &Changes, desc: &str, path: &str, httpmethod: call::CallMethod, uploaddata: Option<&INPUT>) -> GGRResult<OUTPUT>
where INPUT: serde::Serialize + std::fmt::Debug,
OUTPUT: serde::de::DeserializeOwned
{
match c.call.request(httpmethod, path, uploaddata) {
Ok(cr) => {
match cr.status() {
200 | 201 | 202 | 203 | 205 => cr.convert::<OUTPUT>(),
status => { Err(GGRError::GerritApiError(GerritError::GerritApi(status, String::from_utf8(cr.get_body().unwrap_or_else(|| "no cause from server".into()))?))) },
}
},
Err(x) => {
Err(GGRError::General(format!("Problem '{}' with {}", x, desc)))
}
}
}
pub fn query_changes<S>(&mut self, querylist: Option<Vec<S>>, labellist: Option<Vec<S>>) -> GGRResult<Vec<entities::ChangeInfo>>
where S: Into<String> {
let mut querystring = format!("pp=0{}", Changes::build_query_string(querylist));
let labelstring = Changes::build_label_string(labellist);
if ! labelstring.is_empty() {
querystring = format!("{}&{}", querystring, labelstring);
}
querystring = querystring.replace("//", "/");
querystring = querystring.replace("//", "/");
querystring = querystring.replace("//", "/");
self.call.set_url_query(Some(&querystring));
let path = format!("{}/", ENDPOINT);
Changes::execute::<(),Vec<entities::ChangeInfo>>(self, "query change", &path, call::CallMethod::Get, None)
}
pub fn create_change(&self, ci: &entities::ChangeInput) -> GGRResult<entities::ChangeInfo> {
if ci.project.is_empty() || ci.branch.is_empty() || ci.subject.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeInputProblem));
}
let config = config::Config::new(self.call.get_base());
if let Err(x) = config.check_version("POST /changes/".into(), "2.10.0".into()) {
return Err(x);
}
Changes::execute(self, "change create", ENDPOINT, call::CallMethod::Post, Some(&ci))
}
pub fn get_change(&mut self, changeid: &str, features: Option<Vec<&str>>) -> GGRResult<entities::ChangeInfo> {
if changeid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let query = Changes::build_label_string(features);
let path = format!("{}/{}", ENDPOINT, changeid);
self.call.set_url_query(Some(&query));
Changes::execute::<(),entities::ChangeInfo>(self, "get change", &path, call::CallMethod::Get, None)
}
pub fn get_change_detail(&self, changeid: &str) -> GGRResult<entities::ChangeInfo> {
if changeid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let path = format!("{}/{}/detail", ENDPOINT, changeid);
Changes::execute::<(),entities::ChangeInfo>(self, "get change detail", &path, call::CallMethod::Get, None)
}
pub fn get_reviewers(&self, changeid: &str) -> GGRResult<Vec<entities::ReviewerInfo>> {
if changeid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let path = format!("{}/{}/reviewers/", ENDPOINT, changeid);
Changes::execute::<(),Vec<entities::ReviewerInfo>>(self, "receiving reviewer list", &path, call::CallMethod::Get, None)
}
pub fn add_reviewer(&self, changeid: &str, reviewer: &str) -> GGRResult<entities::AddReviewerResult> {
if changeid.is_empty() || reviewer.is_empty() {
return Err(GGRError::GerritApiError(GerritError::GetReviewerListProblem("changeid or reviewer is empty".into())));
}
let path = format!("{}/{}/reviewers", ENDPOINT, changeid);
let reviewerinput = entities::ReviewerInput {
reviewer: reviewer.into(),
confirmed: None,
state: None,
};
Changes::execute::<&entities::ReviewerInput,entities::AddReviewerResult>(self, "add reviewer", &path, call::CallMethod::Post, Some(&&reviewerinput))
}
pub fn delete_reviewer(&self, changeid: &str, reviewer: &str) -> GGRResult<()> {
if changeid.is_empty() || reviewer.is_empty() {
return Err(GGRError::GerritApiError(GerritError::GetReviewerListProblem("changeid or reviewer is empty".into())));
}
let path = format!("{}/{}/reviewers/{}", ENDPOINT, changeid, reviewer);
Changes::execute::<(),()>(self, "deleting reviewer", &path, call::CallMethod::Delete, None)
}
pub fn abandon_change(&self, changeid: &str, message: Option<&str>, notify: Option<&str>) -> GGRResult<entities::ChangeInfo> {
if changeid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let path = format!("{}/{}/abandon", ENDPOINT, changeid);
let notify = match notify {
Some(notify) => {
match notify {
"all" => Some(entities::AbandonInputNotify::ALL),
"owner" => Some(entities::AbandonInputNotify::OWNER),
"owner_reviewer" => Some(entities::AbandonInputNotify::OWNER_REVIEWERS),
_ => Some(entities::AbandonInputNotify::NONE),
}
},
None => None
};
let abandoninput = entities::AbandonInput {
message: message.map(|s| s.to_string()),
notify: notify,
};
Changes::execute::<&entities::AbandonInput,entities::ChangeInfo>(self, "abandon change", &path, call::CallMethod::Post, Some(&&abandoninput))
}
pub fn restore_change(&self, changeid: &str, message: Option<&str>) -> GGRResult<entities::ChangeInfo> {
if changeid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let path = format!("{}/{}/restore", ENDPOINT, changeid);
let restoreinput = entities::RestoreInput {
message: message.map(|s| s.to_string()),
};
Changes::execute::<&entities::RestoreInput,entities::ChangeInfo>(self, "restore change", &path, call::CallMethod::Post, Some(&&restoreinput))
}
pub fn set_review(&self, changeid: &str, revisionid: &str, message: Option<&str>, labels: Option<entities::ReviewInfo>) -> GGRResult<entities::ReviewInfo> {
if changeid.is_empty() || revisionid.is_empty() {
return Err(GGRError::GerritApiError(GerritError::ChangeIDEmpty));
}
let path = format!("{}/{}/revisions/{}/review", ENDPOINT, changeid, revisionid);
use std::collections::HashMap;
#[derive(Serialize, Debug)]
struct Review {
message: Option<String>,
labels: HashMap<String, i8>,
};
let review = Review {
message: message.map(|s| s.to_string()),
labels: labels.unwrap_or(entities::ReviewInfo{ labels: HashMap::new() }).labels,
};
Changes::execute::<&Review,entities::ReviewInfo>(self, "set review", &path, call::CallMethod::Post, Some(&&review))
}
}