Skip to content
Clone
89109d2
137 lines 4.4 KB Rust
use crate::rpcgit::commit::types::{CommitInfo, ListCommitsOptions};
use serde::{Deserialize, Serialize};
use crate::service::GitService;
use crate::service::cache::{self, IMMUTABLE_TTL};
use crate::share::errors::{GitError, map_baby};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogListDto {
pub commits: Vec<CommitInfo>,
pub cursor: Option<String>,
pub has_more: bool,
}
#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
pub struct LogOptions {
#[serde(default)]
pub revision: Option<String>,
#[serde(default)]
pub limit: Option<u64>,
#[serde(default)]
pub skip: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNode {
pub commit: CommitInfo,
pub lanes: Vec<usize>,
}
const DEFAULT_LOG_LIMIT: u64 = 50;
const MAX_LOG_LIMIT: u64 = 200;
impl GitService {
pub async fn log_list(
&self,
space_name: String,
repo_name: String,
opts: LogOptions,
) -> Result<LogListDto, GitError> {
let limit = opts
.limit
.unwrap_or(DEFAULT_LOG_LIMIT)
.clamp(1, MAX_LOG_LIMIT);
let skip = opts.skip.unwrap_or(0);
let revision = opts.revision.clone().unwrap_or_else(|| "HEAD".to_string());
let ctx = self.context(space_name, repo_name).await?;
let baby = self.baby(&ctx);
let rev_oid = baby.rev_parse(&revision).await.map_err(map_baby)?;
let key = format!(
"repo.{}.log_list.{}:{}:{}",
ctx.model.id, rev_oid, skip, limit
);
cache::cached_fetch(&ctx.cache, &key, IMMUTABLE_TTL, || async {
let max = limit + skip + 1;
let max_count = if max <= u32::MAX as u64 {
Some(max as u32)
} else {
None
};
let commits = baby
.list_commits(ListCommitsOptions {
revision: rev_oid.to_string(),
max_count,
..Default::default()
})
.await
.map_err(map_baby)?;
let has_more = commits.len() > limit as usize + skip as usize;
let mut page = commits;
if page.len() > limit as usize + skip as usize {
page.truncate(limit as usize + skip as usize);
}
let page = page.into_iter().skip(skip as usize).collect::<Vec<_>>();
let cursor = page.last().map(|c| c.id.to_string());
Ok(LogListDto {
commits: page,
cursor: if has_more { cursor } else { None },
has_more,
})
})
.await
}
pub async fn log_commits_between(
&self,
space_name: String,
repo_name: String,
after: String,
before: String,
limit: u64,
) -> Result<LogListDto, GitError> {
let limit = limit.clamp(1, MAX_LOG_LIMIT);
let ctx = self.context(space_name, repo_name).await?;
let baby = self.baby(&ctx);
let after_oid = baby.rev_parse(&after).await.map_err(map_baby)?;
let before_oid = baby.rev_parse(&before).await.map_err(map_baby)?;
let key = format!(
"repo.{}.log_between.{}.{}:{}",
ctx.model.id, after_oid, before_oid, limit
);
cache::cached_fetch(&ctx.cache, &key, IMMUTABLE_TTL, || async {
let commits = baby
.list_commits(ListCommitsOptions {
revision: format!("{after_oid}..{before_oid}"),
max_count: Some(limit as u32),
..Default::default()
})
.await
.map_err(map_baby)?;
Ok(LogListDto {
commits,
cursor: None,
has_more: false,
})
})
.await
}
pub async fn log_last_commit(
&self,
space_name: String,
repo_name: String,
branch: String,
) -> Result<CommitInfo, GitError> {
let ctx = self.context(space_name, repo_name).await?;
let baby = self.baby(&ctx);
let rev_oid = baby.rev_parse(&branch).await.map_err(map_baby)?;
let key = format!("repo.{}.commit.{}", ctx.model.id, rev_oid);
cache::cached_fetch(&ctx.cache, &key, IMMUTABLE_TTL, || async {
baby.get_commit(&branch).await.map_err(map_baby)
})
.await
}
}