git_cliff_core/remote/
bitbucket.rsuse crate::config::Remote;
use crate::error::*;
use reqwest_middleware::ClientWithMiddleware;
use serde::{
Deserialize,
Serialize,
};
use std::env;
use super::*;
const BITBUCKET_API_URL: &str = "https://api.bitbucket.org/2.0/repositories";
const BITBUCKET_API_URL_ENV: &str = "BITBUCKET_API_URL";
pub const START_FETCHING_MSG: &str = "Retrieving data from Bitbucket...";
pub const FINISHED_FETCHING_MSG: &str = "Done fetching Bitbucket data.";
pub(crate) const TEMPLATE_VARIABLES: &[&str] =
&["bitbucket", "commit.bitbucket", "commit.remote"];
pub(crate) const BITBUCKET_MAX_PAGE_PRS: usize = 50;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BitbucketCommit {
pub hash: String,
pub author: Option<BitbucketCommitAuthor>,
}
impl RemoteCommit for BitbucketCommit {
fn id(&self) -> String {
self.hash.clone()
}
fn username(&self) -> Option<String> {
self.author.clone().and_then(|v| v.login)
}
}
impl RemoteEntry for BitbucketPagination<BitbucketCommit> {
fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
let commit_page = page + 1;
format!(
"{}/{}/{}/commits?pagelen={MAX_PAGE_SIZE}&page={commit_page}",
api_url, remote.owner, remote.repo
)
}
fn buffer_size() -> usize {
10
}
fn early_exit(&self) -> bool {
self.values.is_empty()
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BitbucketPagination<T> {
pub size: Option<i64>,
pub page: Option<i64>,
pub pagelen: Option<i64>,
pub next: Option<String>,
pub previous: Option<String>,
pub values: Vec<T>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BitbucketCommitAuthor {
#[serde(rename = "raw")]
pub login: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PullRequestLabel {
pub name: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BitbucketPullRequestMergeCommit {
pub hash: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BitbucketPullRequest {
pub id: i64,
pub title: Option<String>,
pub merge_commit_sha: BitbucketPullRequestMergeCommit,
pub author: BitbucketCommitAuthor,
}
impl RemotePullRequest for BitbucketPullRequest {
fn number(&self) -> i64 {
self.id
}
fn title(&self) -> Option<String> {
self.title.clone()
}
fn labels(&self) -> Vec<String> {
vec![]
}
fn merge_commit(&self) -> Option<String> {
Some(self.merge_commit_sha.hash.clone())
}
}
impl RemoteEntry for BitbucketPagination<BitbucketPullRequest> {
fn url(_id: i64, api_url: &str, remote: &Remote, page: i32) -> String {
let pr_page = page + 1;
format!(
"{}/{}/{}/pullrequests?&pagelen={BITBUCKET_MAX_PAGE_PRS}&\
page={pr_page}&state=MERGED",
api_url, remote.owner, remote.repo
)
}
fn buffer_size() -> usize {
5
}
fn early_exit(&self) -> bool {
self.values.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct BitbucketClient {
remote: Remote,
client: ClientWithMiddleware,
}
impl TryFrom<Remote> for BitbucketClient {
type Error = Error;
fn try_from(remote: Remote) -> Result<Self> {
Ok(Self {
client: create_remote_client(&remote, "application/json")?,
remote,
})
}
}
impl RemoteClient for BitbucketClient {
fn api_url() -> String {
env::var(BITBUCKET_API_URL_ENV)
.ok()
.unwrap_or_else(|| BITBUCKET_API_URL.to_string())
}
fn remote(&self) -> Remote {
self.remote.clone()
}
fn client(&self) -> ClientWithMiddleware {
self.client.clone()
}
}
impl BitbucketClient {
pub async fn get_commits(&self) -> Result<Vec<Box<dyn RemoteCommit>>> {
Ok(self
.fetch_with_early_exit::<BitbucketPagination<BitbucketCommit>>(0)
.await?
.into_iter()
.flat_map(|v| v.values)
.map(|v| Box::new(v) as Box<dyn RemoteCommit>)
.collect())
}
pub async fn get_pull_requests(
&self,
) -> Result<Vec<Box<dyn RemotePullRequest>>> {
Ok(self
.fetch_with_early_exit::<BitbucketPagination<BitbucketPullRequest>>(0)
.await?
.into_iter()
.flat_map(|v| v.values)
.map(|v| Box::new(v) as Box<dyn RemotePullRequest>)
.collect())
}
}