gitdataai/lib/git/graphql/branch.rs
2026-05-30 01:38:40 +08:00

174 lines
4.6 KiB
Rust

use std::time::Duration;
use juniper::graphql_object;
use serde::{Deserialize, Serialize};
use crate::{
cmd::branch::{branch_list::BranchListItem, branch_summary::BranchSummary},
graphql::{
GraphqlContext,
cache_helper::{
MUTABLE_TTL, cached_json, mutable_cache_key, repo_revision,
},
},
};
const BRANCH_TTL: Duration = MUTABLE_TTL;
#[derive(Clone, Serialize, Deserialize)]
pub struct BranchGql {
pub name: String,
pub oid: String,
pub is_head: bool,
pub is_remote: bool,
pub is_current: bool,
pub upstream: Option<String>,
}
#[graphql_object(context = GraphqlContext)]
impl BranchGql {
fn name(&self) -> &str {
&self.name
}
fn oid(&self) -> &str {
&self.oid
}
fn is_head(&self) -> bool {
self.is_head
}
fn is_remote(&self) -> bool {
self.is_remote
}
fn is_current(&self) -> bool {
self.is_current
}
fn upstream(&self) -> Option<&str> {
self.upstream.as_deref()
}
}
impl From<BranchListItem> for BranchGql {
fn from(item: BranchListItem) -> Self {
BranchGql {
name: item.name,
oid: item.oid.0,
is_head: item.is_head,
is_remote: item.is_remote,
is_current: item.is_current,
upstream: item.upstream,
}
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct BranchSummaryGql {
pub local_count: i32,
pub remote_count: i32,
pub all_count: i32,
}
#[graphql_object(context = GraphqlContext)]
impl BranchSummaryGql {
fn local_count(&self) -> i32 {
self.local_count
}
fn remote_count(&self) -> i32 {
self.remote_count
}
fn all_count(&self) -> i32 {
self.all_count
}
}
impl From<BranchSummary> for BranchSummaryGql {
fn from(s: BranchSummary) -> Self {
BranchSummaryGql {
local_count: s.local_count as i32,
remote_count: s.remote_count as i32,
all_count: s.all_count as i32,
}
}
}
pub async fn resolve_branches(
ctx: &GraphqlContext,
) -> anyhow::Result<Vec<BranchGql>> {
let revision = repo_revision(ctx).await;
let key = mutable_cache_key(ctx, "query:git:branches", &[], &revision);
cached_json(&ctx.cache, &key, BRANCH_TTL, || {
let repo = ctx.repo.clone();
async move {
let items =
tokio::task::spawn_blocking(move || repo.branch_list_all())
.await?
.map_err(|e| anyhow::anyhow!(e))?;
Ok(items.into_iter().map(BranchGql::from).collect())
}
})
.await
}
pub async fn resolve_branch(
ctx: &GraphqlContext,
name: String,
) -> anyhow::Result<BranchGql> {
let revision = repo_revision(ctx).await;
let key = mutable_cache_key(ctx, "query:git:branch", &[&name], &revision);
cached_json(&ctx.cache, &key, BRANCH_TTL, || {
let repo = ctx.repo.clone();
let branch_name = name.clone();
async move {
let item = tokio::task::spawn_blocking(move || {
repo.branch_info(branch_name)
})
.await?
.map_err(|e| anyhow::anyhow!(e))?;
Ok(BranchGql::from(item))
}
})
.await
}
pub async fn resolve_head_branch(
ctx: &GraphqlContext,
) -> anyhow::Result<BranchGql> {
let revision = repo_revision(ctx).await;
let key = mutable_cache_key(ctx, "query:git:head_branch", &[], &revision);
cached_json(&ctx.cache, &key, BRANCH_TTL, || {
let repo1 = ctx.repo.clone();
let repo2 = ctx.repo.clone();
async move {
let head_name =
tokio::task::spawn_blocking(move || repo1.branch_head_name())
.await?
.map_err(|e| anyhow::anyhow!(e))?;
let branch = tokio::task::spawn_blocking(move || {
repo2.branch_info(head_name)
})
.await?
.map_err(|e| anyhow::anyhow!(e))?;
Ok(BranchGql::from(branch))
}
})
.await
}
pub async fn resolve_branch_summary(
ctx: &GraphqlContext,
) -> anyhow::Result<BranchSummaryGql> {
let revision = repo_revision(ctx).await;
let key =
mutable_cache_key(ctx, "query:git:branch_summary", &[], &revision);
cached_json(&ctx.cache, &key, BRANCH_TTL, || {
let repo = ctx.repo.clone();
async move {
let summary =
tokio::task::spawn_blocking(move || repo.branch_summary())
.await?
.map_err(|e| anyhow::anyhow!(e))?;
Ok(BranchSummaryGql::from(summary))
}
})
.await
}