use std::collections::HashSet;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use parking_lot::Mutex;
use reqwest::Url;
use reqwest_middleware::ClientWithMiddleware;
use crate::Error;
use crate::api::catalog::{
Branch, BranchEventPage, BranchId, BranchName, CatalogApi, CatalogClient, Commit, CommitId,
CommitLog, CompareCommitsRequest, CompareCommitsResponse, CreateBranchRequest,
CreateTagRequest, DeleteBranchRequest, DiffStateRequest, DiffStateResponse,
ListBranchesRequest, ListBranchesResponse, ListTagsRequest, ListTagsResponse,
MergeBasesRequest, MergeBasesResponse, MergeRequest, MergeResponse, OperationId, ProjectId,
PublishOperationRequest, PublishOperationResponse, PublishRequest, PublishResponse,
ReadBranchRequest, ReadCommitLogRequest, ReadOperationResponse, ReadRefEventsRequest,
ReadStateRequest, RecoverPublicationRequest, RecoverPublicationResponse, RefEventPage,
RenameBranchRequest, ResolveCommitRequest, ResolveCommitResponse, ResolvePathRequest,
ResolvePathResponse, RevertRequest, RevertResponse, StatePage, Tag,
};
use crate::auth::TokenSource;
use crate::http::{HttpClient, api_http_client};
#[derive(Clone)]
pub struct Client {
http: HttpClient,
client: CatalogClient,
initialized_projects: Arc<Mutex<HashSet<String>>>,
}
impl Debug for Client {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Client")
.field("base_url", self.http.base_url())
.finish_non_exhaustive()
}
}
impl Client {
pub fn new(base_url: &str) -> Result<Self, Error> {
let base = Url::parse(base_url).map_err(Error::invalid)?;
let client = api_http_client().map_err(Error::invalid)?;
Self::from_http(HttpClient::new(base, client, None))
}
pub(crate) fn from_http(http: HttpClient) -> Result<Self, Error> {
let client =
CatalogClient::from_transport(http.base_url().as_str(), http.middleware_client())
.map_err(Error::from)?;
Ok(Self {
http,
client,
initialized_projects: Arc::default(),
})
}
pub fn with_client(mut self, client: impl Into<ClientWithMiddleware>) -> Self {
self.http = self.http.with_client(client);
self.client = self.client.with_transport(self.http.middleware_client());
self
}
pub fn with_token_source(mut self, provider: TokenSource) -> Self {
self.http = self.http.with_token_source(provider);
self
}
pub async fn initialize_journal(&self, project_id: &impl AsRef<str>) -> Result<Branch, Error> {
let branch = self
.client
.initialize(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
)
.await?;
self.initialized_projects
.lock()
.insert(project_id.as_ref().to_owned());
Ok(branch)
}
pub async fn pin_journal(
&self,
project_id: &impl AsRef<str>,
branch: &BranchName,
) -> Result<Branch, Error> {
self.client
.read_branch(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
ReadBranchRequest {
name: branch.clone(),
},
)
.await
.map_err(Error::from)
}
pub async fn resolve_commit(
&self,
project_id: &impl AsRef<str>,
request: &ResolveCommitRequest,
) -> Result<ResolveCommitResponse, Error> {
self.client
.resolve_commit(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn resolve_journal_path(
&self,
project_id: &impl AsRef<str>,
commit_id: CommitId,
request: &ResolvePathRequest,
) -> Result<ResolvePathResponse, Error> {
self.client
.resolve_path(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
commit_id,
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn publish_operation(
&self,
project_id: &impl AsRef<str>,
request: &PublishOperationRequest,
) -> Result<PublishOperationResponse, Error> {
self.ensure_journal(project_id).await?;
self.client
.publish_operation(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn read_operation(
&self,
project_id: &impl AsRef<str>,
operation_id: OperationId,
) -> Result<ReadOperationResponse, Error> {
self.client
.read_operation(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
operation_id,
)
.await
.map_err(Error::from)
}
pub async fn publish_journal(
&self,
project_id: &impl AsRef<str>,
request: &PublishRequest,
) -> Result<PublishResponse, Error> {
self.ensure_journal(project_id).await?;
self.client
.publish(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn read_journal_commit(
&self,
project_id: &impl AsRef<str>,
commit_id: CommitId,
) -> Result<Commit, Error> {
self.client
.read_commit(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
commit_id,
)
.await
.map_err(Error::from)
}
pub async fn read_journal_state(
&self,
project_id: &impl AsRef<str>,
commit_id: CommitId,
request: &ReadStateRequest,
) -> Result<StatePage, Error> {
self.client
.read_state(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
commit_id,
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn list_journal_branches(
&self,
project_id: &impl AsRef<str>,
request: &ListBranchesRequest,
) -> Result<ListBranchesResponse, Error> {
self.client
.list_branches(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn create_journal_branch(
&self,
project_id: &impl AsRef<str>,
request: &CreateBranchRequest,
) -> Result<Branch, Error> {
self.ensure_journal(project_id).await?;
self.client
.create_branch(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn rename_journal_branch(
&self,
project_id: &impl AsRef<str>,
branch_id: BranchId,
request: &RenameBranchRequest,
) -> Result<Branch, Error> {
self.ensure_journal(project_id).await?;
self.client
.rename_branch(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
branch_id,
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn delete_journal_branch(
&self,
project_id: &impl AsRef<str>,
branch_id: BranchId,
request: &DeleteBranchRequest,
) -> Result<Branch, Error> {
self.ensure_journal(project_id).await?;
self.client
.delete_branch(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
branch_id,
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn read_branch_reflog(
&self,
project_id: &impl AsRef<str>,
branch_id: BranchId,
request: &ReadRefEventsRequest,
) -> Result<BranchEventPage, Error> {
self.client
.read_branch_events(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
branch_id,
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn read_project_ref_events(
&self,
project_id: &impl AsRef<str>,
request: &ReadRefEventsRequest,
) -> Result<RefEventPage, Error> {
self.client
.read_ref_events(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn create_journal_tag(
&self,
project_id: &impl AsRef<str>,
request: &CreateTagRequest,
) -> Result<Tag, Error> {
self.ensure_journal(project_id).await?;
self.client
.create_tag(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn list_journal_tags(
&self,
project_id: &impl AsRef<str>,
request: &ListTagsRequest,
) -> Result<ListTagsResponse, Error> {
self.client
.list_tags(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn merge_bases(
&self,
project_id: &impl AsRef<str>,
request: &MergeBasesRequest,
) -> Result<MergeBasesResponse, Error> {
self.client
.merge_bases(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn compare_commits(
&self,
project_id: &impl AsRef<str>,
request: &CompareCommitsRequest,
) -> Result<CompareCommitsResponse, Error> {
self.client
.compare_commits(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn diff_project_state(
&self,
project_id: &impl AsRef<str>,
request: &DiffStateRequest,
) -> Result<DiffStateResponse, Error> {
self.client
.diff_state(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn recover_publication(
&self,
project_id: &impl AsRef<str>,
request: &RecoverPublicationRequest,
) -> Result<RecoverPublicationResponse, Error> {
self.ensure_journal(project_id).await?;
self.client
.recover_publication(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn revert_project_commit(
&self,
project_id: &impl AsRef<str>,
request: &RevertRequest,
) -> Result<RevertResponse, Error> {
self.ensure_journal(project_id).await?;
self.client
.revert(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn merge_journal(
&self,
project_id: &impl AsRef<str>,
request: &MergeRequest,
) -> Result<MergeResponse, Error> {
self.ensure_journal(project_id).await?;
self.client
.merge(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
pub async fn read_commit_log(
&self,
project_id: &impl AsRef<str>,
request: &ReadCommitLogRequest,
) -> Result<CommitLog, Error> {
self.client
.read_commit_log(
self.http.auth_header().await?,
ProjectId(project_id.as_ref().to_owned()),
request.clone(),
)
.await
.map_err(Error::from)
}
async fn ensure_journal(&self, project_id: &impl AsRef<str>) -> Result<(), Error> {
if self
.initialized_projects
.lock()
.contains(project_id.as_ref())
{
return Ok(());
}
self.initialize_journal(project_id).await.map(|_| ())
}
}