use crate::config::Remote;
use crate::error::*;
use reqwest_middleware::ClientWithMiddleware;
use serde::{
Deserialize,
Serialize,
};
use std::env;
use super::*;
const GITLAB_API_URL: &str = "https://gitlab.com/api/v4";
const GITLAB_API_URL_ENV: &str = "GITLAB_API_URL";
pub const START_FETCHING_MSG: &str = "Retrieving data from GitLab...";
pub const FINISHED_FETCHING_MSG: &str = "Done fetching GitLab data.";
pub(crate) const TEMPLATE_VARIABLES: &[&str] =
&["gitlab", "commit.gitlab", "commit.remote"];
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GitLabProject {
pub id: i64,
pub description: Option<String>,
pub name: String,
pub name_with_namespace: String,
pub path_with_namespace: String,
pub created_at: String,
pub default_branch: String,
}
impl RemoteEntry for GitLabProject {
fn url(_id: i64, api_url: &str, remote: &Remote, _page: i32) -> String {
format!(
"{}/projects/{}%2F{}",
api_url,
urlencoding::encode(remote.owner.as_str()),
remote.repo
)
}
fn buffer_size() -> usize {
1
}
fn early_exit(&self) -> bool {
false
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GitLabCommit {
pub id: String,
pub short_id: String,
pub title: String,
pub author_name: String,
pub author_email: String,
pub authored_date: String,
pub committer_name: String,
pub committer_email: String,
pub committed_date: String,
pub created_at: String,
pub message: String,
pub parent_ids: Vec<String>,
pub web_url: String,
}
impl RemoteCommit for GitLabCommit {
fn id(&self) -> String {
self.id.clone()
}
fn username(&self) -> Option<String> {
Some(self.author_name.clone())
}
}
impl RemoteEntry for GitLabCommit {
fn url(id: i64, api_url: &str, _remote: &Remote, page: i32) -> String {
let commit_page = page + 1;
format!(
"{}/projects/{}/repository/commits?per_page={MAX_PAGE_SIZE}&\
page={commit_page}",
api_url, id
)
}
fn buffer_size() -> usize {
10
}
fn early_exit(&self) -> bool {
false
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GitLabMergeRequest {
pub id: i64,
pub iid: i64,
pub project_id: i64,
pub title: String,
pub description: String,
pub state: String,
pub created_at: String,
pub author: GitLabUser,
pub sha: String,
pub merge_commit_sha: Option<String>,
pub squash_commit_sha: Option<String>,
pub web_url: String,
pub labels: Vec<String>,
}
impl RemotePullRequest for GitLabMergeRequest {
fn number(&self) -> i64 {
self.iid
}
fn title(&self) -> Option<String> {
Some(self.title.clone())
}
fn labels(&self) -> Vec<String> {
self.labels.clone()
}
fn merge_commit(&self) -> Option<String> {
self.merge_commit_sha.clone()
}
}
impl RemoteEntry for GitLabMergeRequest {
fn url(id: i64, api_url: &str, _remote: &Remote, page: i32) -> String {
format!(
"{}/projects/{}/merge_requests?per_page={MAX_PAGE_SIZE}&page={page}&\
state=merged",
api_url, id
)
}
fn buffer_size() -> usize {
5
}
fn early_exit(&self) -> bool {
false
}
}
#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
pub struct GitLabUser {
pub id: i64,
pub name: String,
pub username: String,
pub state: String,
pub avatar_url: Option<String>,
pub web_url: String,
}
#[derive(Debug, Default, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
pub struct GitLabReference {
pub short: String,
pub relative: String,
pub full: String,
}
#[derive(Debug, Clone)]
pub struct GitLabClient {
remote: Remote,
client: ClientWithMiddleware,
}
impl TryFrom<Remote> for GitLabClient {
type Error = Error;
fn try_from(remote: Remote) -> Result<Self> {
Ok(Self {
client: create_remote_client(&remote, "application/json")?,
remote,
})
}
}
impl RemoteClient for GitLabClient {
fn api_url() -> String {
env::var(GITLAB_API_URL_ENV)
.ok()
.unwrap_or_else(|| GITLAB_API_URL.to_string())
}
fn remote(&self) -> Remote {
self.remote.clone()
}
fn client(&self) -> ClientWithMiddleware {
self.client.clone()
}
}
impl GitLabClient {
pub async fn get_project(&self) -> Result<GitLabProject> {
self.get_entry::<GitLabProject>(0, 1).await
}
pub async fn get_commits(
&self,
project_id: i64,
) -> Result<Vec<Box<dyn RemoteCommit>>> {
Ok(self
.fetch::<GitLabCommit>(project_id)
.await?
.into_iter()
.map(|v| Box::new(v) as Box<dyn RemoteCommit>)
.collect())
}
pub async fn get_merge_requests(
&self,
project_id: i64,
) -> Result<Vec<Box<dyn RemotePullRequest>>> {
Ok(self
.fetch::<GitLabMergeRequest>(project_id)
.await?
.into_iter()
.map(|v| Box::new(v) as Box<dyn RemotePullRequest>)
.collect())
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn gitlab_remote_encodes_owner() {
let remote = Remote::new("abc/def", "xyz1");
assert_eq!(
"https://gitlab.test.com/api/v4/projects/abc%2Fdef%2Fxyz1",
GitLabProject::url(1, "https://gitlab.test.com/api/v4", &remote, 0)
);
}
}