use crate::AppService; use crate::error::AppError; use crate::git::MergeOptions; use models::repos::repo; use sea_orm::*; use serde::{Deserialize, Serialize}; use session::Session; #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct DescriptionQuery { pub description: String, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct DescriptionResponse { pub description: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct ConfigGetQuery { pub key: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct ConfigSetRequest { pub key: String, pub value: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct ConfigDeleteQuery { pub key: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct ConfigEntriesQuery { pub prefix: Option, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct ConfigEntryResponse { pub name: String, pub value: String, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct ConfigSnapshotResponse { pub entries: Vec, } impl From for ConfigEntryResponse { fn from(e: git::ConfigEntry) -> Self { Self { name: e.name, value: e.value, } } } impl From for ConfigSnapshotResponse { fn from(s: git::ConfigSnapshot) -> Self { Self { entries: s .entries .into_iter() .map(ConfigEntryResponse::from) .collect(), } } } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct GitUpdateRepoRequest { pub default_branch: Option, } #[derive(Debug, Clone, Serialize, utoipa::ToSchema)] pub struct ConfigBoolResponse { pub key: String, pub value: bool, } #[derive(Debug, Clone, Deserialize)] pub struct MergeAnalysisQuery { pub their_oid: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct MergeRefAnalysisQuery { pub ref_name: String, pub their_oid: String, } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct MergeCommitsRequest { pub local_oid: String, pub remote_oid: String, #[serde(default)] pub find_renames: bool, #[serde(default)] pub fail_on_conflict: bool, #[serde(default)] pub skip_reuc: bool, #[serde(default)] pub no_recursive: bool, #[serde(default = "default_rename_threshold")] pub rename_threshold: u32, #[serde(default)] pub target_limit: u32, #[serde(default)] pub recursion_limit: u32, } fn default_rename_threshold() -> u32 { 50 } #[derive(Debug, Clone, Deserialize, utoipa::ToSchema)] pub struct MergeTreesRequest { pub ancestor_oid: String, pub our_oid: String, pub their_oid: String, #[serde(default)] pub find_renames: bool, #[serde(default)] pub fail_on_conflict: bool, #[serde(default)] pub skip_reuc: bool, #[serde(default)] pub no_recursive: bool, #[serde(default = "default_rename_threshold")] pub rename_threshold: u32, #[serde(default)] pub target_limit: u32, #[serde(default)] pub recursion_limit: u32, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct MergeAnalysisResponse { pub analysis: MergeAnalysisResultInner, pub preference: MergePreferenceResultInner, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct MergeAnalysisResultInner { pub is_none: bool, pub is_normal: bool, pub is_up_to_date: bool, pub is_fast_forward: bool, pub is_unborn: bool, } #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] pub struct MergePreferenceResultInner { pub is_none: bool, pub is_no_fast_forward: bool, pub is_fastforward_only: bool, } impl From for MergeAnalysisResultInner { fn from(r: git::MergeAnalysisResult) -> Self { Self { is_none: r.is_none, is_normal: r.is_normal, is_up_to_date: r.is_up_to_date, is_fast_forward: r.is_fast_forward, is_unborn: r.is_unborn, } } } impl From for MergePreferenceResultInner { fn from(r: git::MergePreferenceResult) -> Self { Self { is_none: r.is_none, is_no_fast_forward: r.is_no_fast_forward, is_fastforward_only: r.is_fastforward_only, } } } #[derive(Debug, Clone, Serialize, utoipa::ToSchema)] pub struct MergeheadInfoResponse { pub oid: String, } impl From for MergeheadInfoResponse { fn from(h: git::MergeheadInfo) -> Self { Self { oid: h.oid.to_string(), } } } impl AppService { pub async fn git_description_get( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let description = domain.description_get()?; Ok(DescriptionResponse { description }) } pub async fn git_description_set( &self, namespace: String, repo_name: String, query: DescriptionQuery, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain.description_set(&query.description)?; Ok(DescriptionResponse { description: query.description, }) } pub async fn git_description_reset( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain.description_reset()?; Ok(DescriptionResponse { description: "Unnamed repository".to_string(), }) } pub async fn git_description_exists( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; Ok(domain.description_exists()) } pub async fn git_config_entries( &self, namespace: String, repo_name: String, query: ConfigEntriesQuery, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let snapshot = domain .config_entries(query.prefix.as_deref()) .map_err(AppError::from)?; Ok(ConfigSnapshotResponse::from(snapshot)) } pub async fn git_config_get( &self, namespace: String, repo_name: String, query: ConfigGetQuery, ctx: &Session, ) -> Result, AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain.config_get(&query.key).map_err(AppError::from) } pub async fn git_config_set( &self, namespace: String, repo_name: String, request: ConfigSetRequest, ctx: &Session, ) -> Result<(), AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain .config_set(&request.key, &request.value) .map_err(AppError::from) } pub async fn git_config_delete( &self, namespace: String, repo_name: String, query: ConfigDeleteQuery, ctx: &Session, ) -> Result<(), AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain.config_delete(&query.key).map_err(AppError::from) } pub async fn git_config_has( &self, namespace: String, repo_name: String, query: ConfigGetQuery, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let exists = domain.config_has(&query.key).map_err(AppError::from)?; Ok(ConfigBoolResponse { key: query.key, value: exists, }) } pub async fn git_merge_analysis( &self, namespace: String, repo_name: String, query: MergeAnalysisQuery, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let their_oid = git::CommitOid::new(&query.their_oid); let (analysis, preference) = domain.merge_analysis(&their_oid).map_err(AppError::from)?; Ok(MergeAnalysisResponse { analysis: MergeAnalysisResultInner::from(analysis), preference: MergePreferenceResultInner::from(preference), }) } pub async fn git_merge_analysis_for_ref( &self, namespace: String, repo_name: String, query: MergeRefAnalysisQuery, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let their_oid = git::CommitOid::new(&query.their_oid); let (analysis, preference) = domain .merge_analysis_for_ref(&query.ref_name, &their_oid) .map_err(AppError::from)?; Ok(MergeAnalysisResponse { analysis: MergeAnalysisResultInner::from(analysis), preference: MergePreferenceResultInner::from(preference), }) } pub async fn git_merge_base( &self, namespace: String, repo_name: String, oid1: String, oid2: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; let base = domain .merge_base(&git::CommitOid::new(&oid1), &git::CommitOid::new(&oid2)) .map_err(AppError::from)?; Ok(base.to_string()) } pub async fn git_merge_commits( &self, namespace: String, repo_name: String, request: MergeCommitsRequest, ctx: &Session, ) -> Result<(), AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let mut opts = MergeOptions::new(); opts = opts.find_renames(request.find_renames); opts = opts.fail_on_conflict(request.fail_on_conflict); opts = opts.skip_reuc(request.skip_reuc); opts = opts.no_recursive(request.no_recursive); opts = opts.rename_threshold(request.rename_threshold); opts = opts.target_limit(request.target_limit); opts = opts.recursion_limit(request.recursion_limit); let domain = git::GitDomain::from_model(repo)?; domain .merge_commits( &git::CommitOid::new(&request.local_oid), &git::CommitOid::new(&request.remote_oid), Some(opts), ) .map_err(AppError::from) } pub async fn git_merge_trees( &self, namespace: String, repo_name: String, request: MergeTreesRequest, ctx: &Session, ) -> Result<(), AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let mut opts = MergeOptions::new(); opts = opts.find_renames(request.find_renames); opts = opts.fail_on_conflict(request.fail_on_conflict); opts = opts.skip_reuc(request.skip_reuc); opts = opts.no_recursive(request.no_recursive); opts = opts.rename_threshold(request.rename_threshold); opts = opts.target_limit(request.target_limit); opts = opts.recursion_limit(request.recursion_limit); let domain = git::GitDomain::from_model(repo)?; domain .merge_trees( &git::CommitOid::new(&request.ancestor_oid), &git::CommitOid::new(&request.our_oid), &git::CommitOid::new(&request.their_oid), Some(opts), ) .map_err(AppError::from) } pub async fn git_merge_abort( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result<(), AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; domain.merge_abort().map_err(AppError::from) } pub async fn git_merge_is_in_progress( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; Ok(domain.merge_is_in_progress()) } pub async fn git_mergehead_list( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result, AppError> { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let mut domain = git::GitDomain::from_model(repo)?; let heads = domain.mergehead_list().map_err(AppError::from)?; Ok(heads.into_iter().map(MergeheadInfoResponse::from).collect()) } pub async fn git_merge_is_conflicted( &self, namespace: String, repo_name: String, ctx: &Session, ) -> Result { let repo = self.utils_find_repo(namespace, repo_name, ctx).await?; let domain = git::GitDomain::from_model(repo)?; Ok(domain.merge_is_conflicted()) } pub async fn git_update_repo( &self, namespace: String, repo_name: String, params: GitUpdateRepoRequest, ctx: &Session, ) -> Result<(), AppError> { let repo = self .utils_find_repo(namespace.clone(), repo_name.clone(), ctx) .await?; let txn = self.db.begin().await?; let mut active: repo::ActiveModel = repo.clone().into_active_model(); if let Some(default_branch) = params.default_branch { active.default_branch = Set(default_branch); } active.update(&txn).await?; txn.commit().await?; Ok(()) } }