Repository: gitbutlerapp/gitbutler
File: crates/but-forge/src/review.rs (Rust)

Code before:

/// Merge a review to it's target branch
pub async fn merge_review(
    preferred_forge_user: &Option<crate::ForgeUser>,
    forge_repo_info: &crate::forge::ForgeRepoInfo,
    review_number: usize,
    storage: &but_forge_storage::Controller,
) -> Result<()> {
    let crate::forge::ForgeRepoInfo {
        forge, owner, repo, ..
    } = forge_repo_info;
    match forge {
        ForgeName::GitHub => {
            let preferred_account = preferred_forge_user.as_ref().and_then(|user| user.github());
            let pr_number = review_number
                .try_into()
                .context("PR: Failed to cast usize to i64, somehow")?;
            let params = but_github::MergePullRequestParams {
                owner,
                repo,
                pr_number,
                commit_message: None,
                commit_title: None,
                merge_method: None,
            };
            but_github::pr::merge(preferred_account, params, storage).await
        }
        ForgeName::GitLab => {
            let preferred_account = preferred_forge_user.as_ref().and_then(|user| user.gitlab());
            let project_id = GitLabProjectId::new(owner, repo);
            let mr_iid = review_number
                .try_into()
                .context("MR: Failed to cast usize to i64, somehow")?;
            let params = but_gitlab::MergeMergeRequestParams {
                project_id,
                mr_iid,
                squash: None,
            };

            but_gitlab::mr::merge(preferred_account, params, storage).await
        }
        _ => Err(Error::msg(format!(
            "Merging reviews for forge {forge:?} is not implemented yet.",
        ))),
    }
}

/// Set a review to automatically merge when all prerequisites are met.
pub async fn set_review_auto_merge_state(
    preferred_forge_user: &Option<crate::ForgeUser>,
    forge_repo_info: &crate::forge::ForgeRepoInfo,

Review comment (bug):
This is so terrible and I tried to make it better once, and failed :/.

All we want is to block-on a tokio future? Can this be so that it re-uses existing runtimes, or creates a new one if needed? I tried it and failed, so strange.

Code after:
/// Get a specific review (e.g. pull request) for a given forge repository
///
/// The resulting review will be cached.
pub fn get_forge_review(
    preferred_forge_user: &Option<crate::ForgeUser>,
    forge_repo_info: &crate::forge::ForgeRepoInfo,
    review_number: usize,
    db: &mut but_db::DbHandle,
    storage: &but_forge_storage::Controller,
) -> Result<ForgeReview> {
    let preferred_forge_user = preferred_forge_user.clone();
    let forge_repo_info = forge_repo_info.clone();
    let storage = storage.clone();

    let review = std::thread::spawn(move || {
        let runtime = tokio::runtime::Runtime::new().map_err(|e| {
            anyhow::anyhow!(
                "Failed fetch review {review_number}: failed to create Tokio runtime: {e}"
            )
        })?;

        runtime.block_on(get_forge_review_inner(
            &preferred_forge_user,
            &forge_repo_info,
            review_number,
            &storage,
        ))
    })
    .join()
    .map_err(|e| {
        anyhow::anyhow!("Failed to fetch review {review_number}: thread panicked: {e:?}")
    })??;

    // Cache the review and ignore any issues, if any.
    crate::db::upsert_review(db, &review).ok();
    Ok(review)
}

/// Merge a review to it's target branch
pub async fn merge_review(
    preferred_forge_user: &Option<crate::ForgeUser>,
    forge_repo_info: &crate::forge::ForgeRepoInfo,
    review_number: usize,
    storage: &but_forge_storage::Controller,
) -> Result<()> {
    let crate::forge::ForgeRepoInfo {
        forge, owner, repo, ..
    } = forge_repo_info;
    match forge {
        ForgeName::GitHub => {
            let preferred_account = preferred_forge_user.as_ref().and_then(|user| user.github());