use std::sync::Arc; use cache::AppCache; use tokio_stream::wrappers::ReceiverStream; use tonic::{Request, Response, Status}; use crate::rpc::{ error::{spawn_blocking_error, to_status}, proto as p, registry::RepoRegistry, }; pub struct DiffServiceImpl { pub registry: Arc, pub cache: AppCache, } type DiffStream = ReceiverStream>; #[tonic::async_trait] impl p::diff_service_server::DiffService for DiffServiceImpl { async fn diff_stats( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let repo_id = inner.repo_id.clone(); let old_str = inner.old_oid.clone().map(|o| o.value).unwrap_or_default(); let new_str = inner.new_oid.clone().map(|o| o.value).unwrap_or_default(); let cache_key = format!( "git:rpc:cache:diff:stats:{}:{}:{}", repo_id, old_str, new_str ); if let Ok(Some(cached)) = self.cache.get::(&cache_key).await { return Ok(Response::new(cached)); } let bare = self.registry.get(&repo_id).await?; let old: crate::cmd::oid::ObjectId = inner.old_oid.unwrap_or_default().into(); let new: crate::cmd::oid::ObjectId = inner.new_oid.unwrap_or_default().into(); let opts = inner.options.map(Into::into); let stats = tokio::task::spawn_blocking(move || { bare.diff_stats(old, new, opts) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; let result = crate::cmd::diff::DiffResult { stats, deltas: vec![], }; let resp = p::DiffStatsResponse { result: Some(result.into()), }; let _ = self.cache.set(&cache_key, &resp).await; Ok(Response::new(resp)) } async fn diff_patch( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let repo_id = inner.repo_id.clone(); let old_str = inner.old_oid.clone().map(|o| o.value).unwrap_or_default(); let new_str = inner.new_oid.clone().map(|o| o.value).unwrap_or_default(); let cache_key = format!( "git:rpc:cache:diff:patch:{}:{}:{}", repo_id, old_str, new_str ); if let Ok(Some(cached)) = self.cache.get::(&cache_key).await { return Ok(Response::new(cached)); } let bare = self.registry.get(&repo_id).await?; let old: crate::cmd::oid::ObjectId = inner.old_oid.unwrap_or_default().into(); let new: crate::cmd::oid::ObjectId = inner.new_oid.unwrap_or_default().into(); let opts = inner.options.map(Into::into); let result = tokio::task::spawn_blocking(move || { bare.diff_patch(old, new, opts) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; let resp = p::DiffPatchResponse { result: Some(result.into()), }; let _ = self.cache.set(&cache_key, &resp).await; Ok(Response::new(resp)) } type DiffStreamStream = DiffStream; async fn diff_stream( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let old: crate::cmd::oid::ObjectId = inner.old_oid.unwrap_or_default().into(); let new: crate::cmd::oid::ObjectId = inner.new_oid.unwrap_or_default().into(); let opts = inner.options.map(Into::into); let (tx, rx) = tokio::sync::mpsc::channel(128); tokio::task::spawn_blocking(move || { let result = bare.diff_patch(old, new, opts); match result { Ok(diff_result) => { for delta in diff_result.deltas { if tx.blocking_send(Ok(delta.into())).is_err() { break; } } } Err(e) => { let _ = tx.blocking_send(Err(to_status(e))); } } }); Ok(Response::new(ReceiverStream::new(rx))) } async fn diff_patch_side_by_side( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let old: crate::cmd::oid::ObjectId = inner.old_oid.unwrap_or_default().into(); let new: crate::cmd::oid::ObjectId = inner.new_oid.unwrap_or_default().into(); let opts = inner.options.map(Into::into); let result = tokio::task::spawn_blocking(move || { bare.diff_patch_side_by_side(old, new, opts) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::DiffPatchSideBySideResponse { result: Some(result.into()), })) } async fn diff_tree_to_tree( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let old_tree: crate::cmd::oid::ObjectId = inner.old_tree.unwrap_or_default().into(); let new_tree: crate::cmd::oid::ObjectId = inner.new_tree.unwrap_or_default().into(); let options = inner.options.map(Into::into); let result = tokio::task::spawn_blocking(move || { bare.diff_tree_to_tree(old_tree, new_tree, options) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::DiffTreeToTreeResponse { result: Some(result.into()), })) } async fn diff_index_to_tree( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let tree_oid: crate::cmd::oid::ObjectId = inner.tree_oid.unwrap_or_default().into(); let options = inner.options.map(Into::into); let result = tokio::task::spawn_blocking(move || { bare.diff_index_to_tree(tree_oid, options) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::DiffIndexToTreeResponse { result: Some(result.into()), })) } }