292 lines
10 KiB
Rust
292 lines
10 KiB
Rust
use std::sync::Arc;
|
|
|
|
use cache::AppCache;
|
|
use tokio_stream::wrappers::ReceiverStream;
|
|
use tonic::{Request, Response, Status};
|
|
use uuid::Uuid;
|
|
|
|
use crate::rpc::{
|
|
error::{spawn_blocking_error, to_status},
|
|
proto as p,
|
|
registry::RepoRegistry,
|
|
};
|
|
use crate::sync::ReceiveSyncService;
|
|
|
|
pub struct CommitServiceImpl {
|
|
pub registry: Arc<RepoRegistry>,
|
|
pub cache: AppCache,
|
|
pub sync: ReceiveSyncService,
|
|
}
|
|
|
|
type CommitHistoryStream = ReceiverStream<Result<p::CommitMeta, Status>>;
|
|
|
|
#[tonic::async_trait]
|
|
impl p::commit_service_server::CommitService for CommitServiceImpl {
|
|
async fn commit_info(
|
|
&self,
|
|
req: Request<p::CommitInfoRequest>,
|
|
) -> Result<Response<p::CommitInfoResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let oid = inner.oid.unwrap_or_default().into();
|
|
let result = tokio::task::spawn_blocking(move || bare.commit_info(oid))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
Ok(Response::new(p::CommitInfoResponse {
|
|
commit: Some(result.into()),
|
|
}))
|
|
}
|
|
|
|
async fn commit_history(
|
|
&self,
|
|
req: Request<p::CommitHistoryRequest>,
|
|
) -> Result<Response<p::CommitHistoryResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let repo_id = inner.repo_id.clone();
|
|
let cache_key = format!(
|
|
"git:rpc:cache:commit:history:{}:{}:{}:{}:{}",
|
|
repo_id, inner.limit, inner.skip, inner.sort, inner.branch.as_deref().unwrap_or("")
|
|
);
|
|
|
|
if let Ok(Some(cached)) = self.cache.get::<p::CommitHistoryResponse>(&cache_key).await {
|
|
return Ok(Response::new(cached));
|
|
}
|
|
|
|
let bare = self.registry.get(&repo_id).await?;
|
|
let params = crate::cmd::commit::CommitWalkParams {
|
|
start_oids: vec![],
|
|
hide_oids: vec![],
|
|
limit: if inner.limit > 0 { Some(inner.limit as usize) } else { None },
|
|
skip: inner.skip as usize,
|
|
first_parent: false,
|
|
sort: inner.sort.into(),
|
|
branch: inner.branch.clone(),
|
|
};
|
|
let result =
|
|
tokio::task::spawn_blocking(move || bare.commit_history(params))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
let resp = p::CommitHistoryResponse {
|
|
commits: result.into_iter().map(Into::into).collect(),
|
|
};
|
|
let _ = self.cache.set(&cache_key, &resp).await;
|
|
Ok(Response::new(resp))
|
|
}
|
|
|
|
type CommitHistoryStreamStream = CommitHistoryStream;
|
|
|
|
async fn commit_history_stream(
|
|
&self,
|
|
req: Request<p::CommitHistoryRequest>,
|
|
) -> Result<Response<CommitHistoryStream>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let params = crate::cmd::commit::CommitWalkParams {
|
|
start_oids: vec![],
|
|
hide_oids: vec![],
|
|
limit: if inner.limit > 0 { Some(inner.limit as usize) } else { None },
|
|
skip: inner.skip as usize,
|
|
first_parent: false,
|
|
sort: inner.sort.into(),
|
|
branch: inner.branch.clone(),
|
|
};
|
|
let (tx, rx) = tokio::sync::mpsc::channel(128);
|
|
tokio::task::spawn_blocking(move || {
|
|
let result = bare.commit_history(params);
|
|
match result {
|
|
Ok(commits) => {
|
|
for c in commits {
|
|
if tx.blocking_send(Ok(c.into())).is_err() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
let _ = tx.blocking_send(Err(to_status(e)));
|
|
}
|
|
}
|
|
});
|
|
Ok(Response::new(ReceiverStream::new(rx)))
|
|
}
|
|
|
|
async fn commit_summary(
|
|
&self,
|
|
req: Request<p::CommitSummaryRequest>,
|
|
) -> Result<Response<p::CommitSummaryResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let repo_id = inner.repo_id.clone();
|
|
let cache_key = format!("git:rpc:cache:commit:summary:{}", repo_id);
|
|
|
|
if let Ok(Some(cached)) = self.cache.get::<p::CommitSummaryResponse>(&cache_key).await {
|
|
return Ok(Response::new(cached));
|
|
}
|
|
|
|
let bare = self.registry.get(&repo_id).await?;
|
|
let result = tokio::task::spawn_blocking(move || bare.commit_summary())
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
let resp = p::CommitSummaryResponse { summary: Some(result.into()) };
|
|
let _ = self.cache.set(&cache_key, &resp).await;
|
|
Ok(Response::new(resp))
|
|
}
|
|
|
|
async fn commit_walk(
|
|
&self,
|
|
req: Request<p::CommitWalkRequest>,
|
|
) -> Result<Response<p::CommitWalkResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let params = inner.params.unwrap_or_default().into();
|
|
let result =
|
|
tokio::task::spawn_blocking(move || bare.commit_walk(params))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
Ok(Response::new(p::CommitWalkResponse {
|
|
commits: result.into_iter().map(Into::into).collect(),
|
|
}))
|
|
}
|
|
|
|
async fn commit_refs(
|
|
&self,
|
|
req: Request<p::CommitRefsRequest>,
|
|
) -> Result<Response<p::CommitRefsResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let result = tokio::task::spawn_blocking(move || {
|
|
let repo = bare.gix_repo()?;
|
|
let head_id = repo.head_id()?.detach();
|
|
let oid = crate::cmd::oid::ObjectId::new(head_id.to_hex().to_string());
|
|
bare.commit_refs(oid)
|
|
})
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
Ok(Response::new(p::CommitRefsResponse {
|
|
refs: result.into_iter().map(Into::into).collect(),
|
|
}))
|
|
}
|
|
|
|
async fn commit_prefix(
|
|
&self,
|
|
req: Request<p::CommitPrefixRequest>,
|
|
) -> Result<Response<p::CommitPrefixResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let prefix = inner.prefix.clone();
|
|
let result = tokio::task::spawn_blocking(move || {
|
|
bare.commit_oid_from_prefix(&prefix)
|
|
})
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
Ok(Response::new(p::CommitPrefixResponse {
|
|
oid: Some(result.into()),
|
|
}))
|
|
}
|
|
|
|
async fn commit_exists(
|
|
&self,
|
|
req: Request<p::CommitExistsRequest>,
|
|
) -> Result<Response<p::CommitExistsResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let oid: crate::cmd::oid::ObjectId =
|
|
inner.oid.unwrap_or_default().into();
|
|
let result =
|
|
tokio::task::spawn_blocking(move || bare.commit_exists(oid))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
Ok(Response::new(p::CommitExistsResponse { exists: result }))
|
|
}
|
|
|
|
async fn cherry_pick(
|
|
&self,
|
|
req: Request<p::CherryPickRequest>,
|
|
) -> Result<Response<p::CherryPickResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let repo_id = inner.repo_id.clone();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let params = inner.params.unwrap_or_default().into();
|
|
let result =
|
|
tokio::task::spawn_blocking(move || bare.commit_pick(params))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
|
|
if let Ok(repo_uid) = Uuid::parse_str(&repo_id) {
|
|
self.sync.send(crate::sync::RepoReceiveSyncTask { repo_uid }).await;
|
|
}
|
|
|
|
Ok(Response::new(p::CherryPickResponse {
|
|
oid: Some(result.into()),
|
|
}))
|
|
}
|
|
|
|
async fn cherry_pick_sequence(
|
|
&self,
|
|
req: Request<p::CherryPickSequenceRequest>,
|
|
) -> Result<Response<p::CherryPickSequenceResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let repo_id = inner.repo_id.clone();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let params = inner.params.unwrap_or_default().into();
|
|
let result = tokio::task::spawn_blocking(move || {
|
|
bare.commit_cherry_pick_sequence(params)
|
|
})
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
|
|
if let Ok(repo_uid) = Uuid::parse_str(&repo_id) {
|
|
self.sync.send(crate::sync::RepoReceiveSyncTask { repo_uid }).await;
|
|
}
|
|
|
|
Ok(Response::new(p::CherryPickSequenceResponse {
|
|
oid: Some(result.into()),
|
|
}))
|
|
}
|
|
|
|
async fn create_commit(
|
|
&self,
|
|
req: Request<p::CreateCommitRequest>,
|
|
) -> Result<Response<p::CreateCommitResponse>, Status> {
|
|
let inner = req.into_inner();
|
|
let repo_id = inner.repo_id.clone();
|
|
let bare = self.registry.get(&inner.repo_id).await?;
|
|
let params = crate::cmd::commit::CreateCommitParams {
|
|
branch: inner.branch.clone(),
|
|
message: inner.message.clone(),
|
|
author_name: inner.author_name.clone(),
|
|
author_email: inner.author_email.clone(),
|
|
committer_name: inner.committer_name.clone(),
|
|
committer_email: inner.committer_email.clone(),
|
|
files: inner
|
|
.files
|
|
.into_iter()
|
|
.map(|f| crate::cmd::commit::FileChange {
|
|
path: f.path,
|
|
content: f.content,
|
|
})
|
|
.collect(),
|
|
};
|
|
let result = tokio::task::spawn_blocking(move || bare.commit_create(params))
|
|
.await
|
|
.map_err(spawn_blocking_error)?
|
|
.map_err(to_status)?;
|
|
|
|
// Trigger sync after write
|
|
if let Ok(repo_uid) = Uuid::parse_str(&repo_id) {
|
|
self.sync.send(crate::sync::RepoReceiveSyncTask { repo_uid }).await;
|
|
}
|
|
|
|
Ok(Response::new(p::CreateCommitResponse {
|
|
oid: Some(result.into()),
|
|
}))
|
|
}
|
|
}
|