gitdataai/lib/service/git/embed.rs
2026-06-01 22:04:38 +08:00

123 lines
3.4 KiB
Rust

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<String>,
pub default_branch: String,
pub visibility: String,
pub size_bytes: i64,
pub is_archived: bool,
pub updated_at: String,
// Language
pub language: Option<String>,
// Stats
pub star_count: i64,
pub fork_count: i64,
// Topics
pub topics: Vec<String>,
}
impl AppService {
pub async fn repo_embed_card(
&self,
ctx: &Session,
wk_name: &str,
repo_name: &str,
) -> Result<RepoEmbedCardResponse, AppError> {
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<Option<String>, 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<Vec<String>, 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<i64, AppError> {
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<i64, AppError> {
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)
}
}