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, } #[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 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 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> { 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 { 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 { 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 { 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 }