use std::sync::Arc; use cache::AppCache; use tonic::{Request, Response, Status}; use crate::rpc::{ error::{spawn_blocking_error, to_status}, proto as p, registry::RepoRegistry, }; pub struct TreeServiceImpl { pub registry: Arc, pub cache: AppCache, } #[tonic::async_trait] impl p::tree_service_server::TreeService for TreeServiceImpl { async fn tree_entries( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let repo_id = inner.repo_id.clone(); let oid_val = inner.oid.clone().map(|o| o.value).unwrap_or_default(); let base_path = inner.base_path.clone(); let want_last = inner.last; if !want_last { let bare = self.registry.get(&repo_id).await?; let oid = inner.oid.unwrap_or_default().into(); let entries = tokio::task::spawn_blocking(move || { use crate::errors::GitError; bare.tree_entries(oid) .map_err(|e| GitError::Internal(format!("tree_entries: {e}"))) .map(|e| e.into_iter().map(Into::into).collect::>()) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; return Ok(Response::new(p::TreeEntriesResponse { entries })); } let cache_key = format!( "git:rpc:cache:tree:entries:{}:{}:{}", repo_id, oid_val, base_path ); if let Ok(Some(cached)) = self.cache.get::>(&cache_key).await { return Ok(Response::new(p::TreeEntriesResponse { entries: cached })); } let bare = self.registry.get(&repo_id).await?; let bare_bg = bare.clone(); let oid = inner.oid.unwrap_or_default().into(); let base_path_bg = base_path.clone(); let cache_bg = self.cache.clone(); let cache_key_bg = cache_key.clone(); let entries = tokio::task::spawn_blocking(move || { use crate::errors::GitError; bare.tree_entries(oid) .map_err(|e| GitError::Internal(format!("tree_entries: {e}"))) .map(|e| e.into_iter().map(Into::into).collect::>()) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; let mut response = entries.clone(); for entry in &mut response { entry.last_commit_message.clear(); entry.last_commit_time.clear(); entry.last_commit_author_name.clear(); entry.last_commit_author_email.clear(); } tokio::task::spawn(async move { let enriched = tokio::task::spawn_blocking(move || { let paths: Vec = entries.iter().map(|e| { if base_path_bg.is_empty() { e.name.clone() } else { format!("{}/{}", base_path_bg, e.name) } }).collect(); let last_commits = match bare_bg.last_commits_for_paths(&paths) { Ok(lc) => lc, Err(_) => return entries, }; let mut out = entries; for (i, info) in last_commits.into_iter().enumerate() { if let Some(info) = info { if let Some(e) = out.get_mut(i) { e.last_commit_message = info.message; e.last_commit_time = info.time; e.last_commit_author_name = info.author_name; e.last_commit_author_email = info.author_email; } } } out }).await; if let Ok(entries) = enriched { let _ = cache_bg.set(&cache_key_bg, &entries).await; } }); Ok(Response::new(p::TreeEntriesResponse { entries: response })) } async fn tree_entry_by_path( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let tree_oid = inner.tree_oid.unwrap_or_default().into(); let path = inner.path; let result = tokio::task::spawn_blocking(move || { bare.tree_entry_by_path(tree_oid, path) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::TreeEntryByPathResponse { entry: Some(result.into()), })) } async fn tree_entry_by_path_from_commit( &self, req: Request, ) -> Result, Status> { let inner = req.into_inner(); let bare = self.registry.get(&inner.repo_id).await?; let commit_oid = inner.commit_oid.unwrap_or_default().into(); let path = inner.path; let result = tokio::task::spawn_blocking(move || { bare.tree_entry_by_path_from_commit(commit_oid, path) }) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::TreeEntryByPathFromCommitResponse { entry: Some(result.into()), })) } async fn resolve_tree( &self, req: Request, ) -> Result, 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.resolve_tree(oid)) .await .map_err(spawn_blocking_error)? .map_err(to_status)?; Ok(Response::new(p::ResolveTreeResponse { info: Some(result.into()), })) } }