225 lines
5.8 KiB
Rust
225 lines
5.8 KiB
Rust
use std::time::Duration;
|
|
|
|
use juniper::graphql_object;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
cmd::{
|
|
commit::{
|
|
CommitMeta, CommitSignature,
|
|
commit_summary::CommitSummary,
|
|
commit_walker::{CommitWalkParams, CommitWalkSort},
|
|
},
|
|
oid::ObjectId,
|
|
},
|
|
graphql::{
|
|
GraphqlContext,
|
|
cache_helper::{
|
|
IMMUTABLE_TTL, MUTABLE_TTL, cache_key, cached_json,
|
|
mutable_cache_key, repo_revision,
|
|
},
|
|
},
|
|
};
|
|
|
|
const COMMIT_TTL: Duration = IMMUTABLE_TTL;
|
|
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct CommitGql {
|
|
pub oid: String,
|
|
pub message: String,
|
|
pub summary: String,
|
|
pub author: CommitSignatureGql,
|
|
pub committer: CommitSignatureGql,
|
|
pub tree_id: String,
|
|
pub parent_ids: Vec<String>,
|
|
pub encoding: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct CommitSignatureGql {
|
|
pub name: String,
|
|
pub email: String,
|
|
pub time_secs: f64,
|
|
pub offset_minutes: i32,
|
|
}
|
|
|
|
#[graphql_object(context = GraphqlContext)]
|
|
impl CommitSignatureGql {
|
|
fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
fn email(&self) -> &str {
|
|
&self.email
|
|
}
|
|
fn time_secs(&self) -> f64 {
|
|
self.time_secs
|
|
}
|
|
fn offset_minutes(&self) -> i32 {
|
|
self.offset_minutes
|
|
}
|
|
}
|
|
|
|
#[graphql_object(context = GraphqlContext)]
|
|
impl CommitGql {
|
|
fn oid(&self) -> &str {
|
|
&self.oid
|
|
}
|
|
fn message(&self) -> &str {
|
|
&self.message
|
|
}
|
|
fn summary(&self) -> &str {
|
|
&self.summary
|
|
}
|
|
fn author(&self) -> &CommitSignatureGql {
|
|
&self.author
|
|
}
|
|
fn committer(&self) -> &CommitSignatureGql {
|
|
&self.committer
|
|
}
|
|
fn tree_id(&self) -> &str {
|
|
&self.tree_id
|
|
}
|
|
fn parent_ids(&self) -> &[String] {
|
|
&self.parent_ids
|
|
}
|
|
fn encoding(&self) -> Option<&str> {
|
|
self.encoding.as_deref()
|
|
}
|
|
}
|
|
|
|
impl From<CommitSignature> for CommitSignatureGql {
|
|
fn from(s: CommitSignature) -> Self {
|
|
CommitSignatureGql {
|
|
name: s.name,
|
|
email: s.email,
|
|
time_secs: s.time_secs as f64,
|
|
offset_minutes: s.offset_minutes,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CommitMeta> for CommitGql {
|
|
fn from(m: CommitMeta) -> Self {
|
|
CommitGql {
|
|
oid: m.oid.0,
|
|
message: m.message,
|
|
summary: m.summary,
|
|
author: CommitSignatureGql::from(m.author),
|
|
committer: CommitSignatureGql::from(m.committer),
|
|
tree_id: m.tree_id.0,
|
|
parent_ids: m.parent_ids.iter().map(|p| p.0.clone()).collect(),
|
|
encoding: m.encoding,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn resolve_commit(
|
|
ctx: &GraphqlContext,
|
|
oid: String,
|
|
) -> anyhow::Result<CommitGql> {
|
|
let key = cache_key("query:git:commit", &[&oid]);
|
|
cached_json(&ctx.cache, &key, COMMIT_TTL, || {
|
|
let repo = ctx.repo.clone();
|
|
let oid_obj = ObjectId::new(&oid);
|
|
async move {
|
|
let meta =
|
|
tokio::task::spawn_blocking(move || repo.commit_info(oid_obj))
|
|
.await?
|
|
.map_err(|e| anyhow::anyhow!(e))?;
|
|
Ok(CommitGql::from(meta))
|
|
}
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn resolve_commit_history(
|
|
ctx: &GraphqlContext,
|
|
limit: Option<i32>,
|
|
skip: Option<i32>,
|
|
sort: Option<String>,
|
|
) -> anyhow::Result<Vec<CommitGql>> {
|
|
let revision = repo_revision(ctx).await;
|
|
let sort_str = sort.as_deref().unwrap_or("time");
|
|
let key_parts = format!(
|
|
"{}:{}:{}:{}",
|
|
limit.map(|l| l.to_string()).unwrap_or_default(),
|
|
skip.map(|s| s.to_string()).unwrap_or_default(),
|
|
sort_str,
|
|
revision,
|
|
);
|
|
let key = mutable_cache_key(
|
|
ctx,
|
|
"query:git:commit_history",
|
|
&[&key_parts],
|
|
&revision,
|
|
);
|
|
cached_json(&ctx.cache, &key, MUTABLE_TTL, || {
|
|
let repo = ctx.repo.clone();
|
|
let walk_sort = match sort_str {
|
|
"topological" => CommitWalkSort::Topological,
|
|
"time" => CommitWalkSort::Time,
|
|
"reverse" => CommitWalkSort::Reverse,
|
|
_ => CommitWalkSort::Time,
|
|
};
|
|
let params = CommitWalkParams {
|
|
limit: limit.map(|l| l as usize),
|
|
skip: skip.unwrap_or(0) as usize,
|
|
sort: walk_sort,
|
|
..Default::default()
|
|
};
|
|
async move {
|
|
let commits = tokio::task::spawn_blocking(move || {
|
|
repo.commit_history(params)
|
|
})
|
|
.await?
|
|
.map_err(|e| anyhow::anyhow!(e))?;
|
|
Ok(commits.into_iter().map(CommitGql::from).collect())
|
|
}
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn resolve_commit_summary(
|
|
ctx: &GraphqlContext,
|
|
) -> anyhow::Result<CommitSummaryGql> {
|
|
let revision = repo_revision(ctx).await;
|
|
let key =
|
|
mutable_cache_key(ctx, "query:git:commit_summary", &[], &revision);
|
|
cached_json(&ctx.cache, &key, MUTABLE_TTL, || {
|
|
let repo = ctx.repo.clone();
|
|
async move {
|
|
let summary =
|
|
tokio::task::spawn_blocking(move || repo.commit_summary())
|
|
.await?
|
|
.map_err(|e| anyhow::anyhow!(e))?;
|
|
Ok(CommitSummaryGql::from(summary))
|
|
}
|
|
})
|
|
.await
|
|
}
|
|
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct CommitSummaryGql {
|
|
pub head: Option<CommitGql>,
|
|
pub count: i32,
|
|
}
|
|
|
|
#[graphql_object(context = GraphqlContext)]
|
|
impl CommitSummaryGql {
|
|
fn head(&self) -> Option<&CommitGql> {
|
|
self.head.as_ref()
|
|
}
|
|
fn count(&self) -> i32 {
|
|
self.count
|
|
}
|
|
}
|
|
|
|
impl From<CommitSummary> for CommitSummaryGql {
|
|
fn from(s: CommitSummary) -> Self {
|
|
CommitSummaryGql {
|
|
head: s.head.map(CommitGql::from),
|
|
count: s.count as i32,
|
|
}
|
|
}
|
|
}
|