use db::sqlx; use serde::{Deserialize, Serialize}; use session::Session; use utoipa::ToSchema; use crate::{AppService, error::AppError}; #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct RepoEmbedCardResponse { // Repo basics pub name: String, pub description: Option, pub default_branch: String, pub visibility: String, pub size_bytes: i64, pub is_archived: bool, pub updated_at: String, // Language pub language: Option, // Stats pub star_count: i64, pub fork_count: i64, // Topics pub topics: Vec, } impl AppService { pub async fn repo_embed_card( &self, ctx: &Session, wk_name: &str, repo_name: &str, ) -> Result { let repo = self.git_require_member(ctx, wk_name, repo_name).await?; // Fetch language, topics, star count, fork count in parallel let (lang, topics, star_count, fork_count) = tokio::try_join!( self.git_repo_embed_language(repo.id), self.git_repo_embed_topics(repo.id), self.git_repo_embed_star_count(repo.id), self.git_repo_embed_fork_count(repo.id), )?; Ok(RepoEmbedCardResponse { name: repo.name, description: repo.description, default_branch: repo.default_branch, visibility: repo.visibility, size_bytes: repo.size_bytes, is_archived: repo.is_archived, updated_at: repo.updated_at.to_rfc3339(), language: lang, star_count, fork_count, topics, }) } async fn git_repo_embed_language( &self, repo_id: uuid::Uuid, ) -> Result, AppError> { let row: Option<(String,)> = sqlx::query_as( "SELECT language FROM repo_language WHERE repo = $1 ORDER BY bytes DESC LIMIT 1", ) .bind(repo_id) .fetch_optional(self.db.reader()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(row.map(|r| r.0)) } async fn git_repo_embed_topics( &self, repo_id: uuid::Uuid, ) -> Result, AppError> { let rows: Vec<(String,)> = sqlx::query_as( "SELECT topic FROM repo_topic WHERE repo = $1 ORDER BY topic", ) .bind(repo_id) .fetch_all(self.db.reader()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(rows.into_iter().map(|r| r.0).collect()) } async fn git_repo_embed_star_count( &self, repo_id: uuid::Uuid, ) -> Result { let row: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM repo_star WHERE repo = $1") .bind(repo_id) .fetch_one(self.db.reader()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(row.0) } async fn git_repo_embed_fork_count( &self, repo_id: uuid::Uuid, ) -> Result { let row: (i64,) = sqlx::query_as( "SELECT COUNT(*) FROM repo_fork f \ INNER JOIN repo r ON r.id = f.repo AND r.deleted_at IS NULL \ WHERE f.source_repo = $1", ) .bind(repo_id) .fetch_one(self.db.reader()) .await .map_err(|e| AppError::DatabaseError(e.to_string()))?; Ok(row.0) } }