a73x

src/server/http/repo/patches.rs

Ref:   Size: 5.0 KiB

use std::sync::Arc;

use axum::extract::{Path, State};
use axum::response::{IntoResponse, Response};

use super::{AppState, CommentView, collab_counts, internal_error, not_found, open_repo};

#[derive(Debug)]
pub struct PatchListItem {
    pub id: String,
    pub short_id: String,
    pub status: String,
    pub title: String,
    pub author: String,
    pub branch: String,
    pub updated: String,
}

#[derive(askama::Template, askama_web::WebTemplate)]
#[template(path = "patches.html")]
pub struct PatchesTemplate {
    pub site_title: String,
    pub repo_name: String,
    pub active_section: String,
    pub open_patches: usize,
    pub open_issues: usize,
    pub patches: Vec<PatchListItem>,
}

#[derive(Debug)]
pub struct RevisionView {
    pub number: u32,
    pub commit: String,
    pub timestamp: String,
    pub body: Option<String>,
}

#[derive(Debug)]
pub struct ReviewView {
    pub author: String,
    pub verdict: String,
    pub body: String,
    pub timestamp: String,
    pub revision: Option<u32>,
}

#[derive(Debug)]
pub struct InlineCommentView {
    pub author: String,
    pub file: String,
    pub line: u32,
    pub body: String,
    pub timestamp: String,
    pub revision: Option<u32>,
}

#[derive(Debug)]
pub struct PatchDetailView {
    pub title: String,
    pub body: String,
    pub status: String,
    pub author: String,
    pub branch: String,
    pub base_ref: String,
    pub revisions: Vec<RevisionView>,
    pub reviews: Vec<ReviewView>,
    pub inline_comments: Vec<InlineCommentView>,
    pub comments: Vec<CommentView>,
}

#[derive(askama::Template, askama_web::WebTemplate)]
#[template(path = "patch_detail.html")]
pub struct PatchDetailTemplate {
    pub site_title: String,
    pub repo_name: String,
    pub active_section: String,
    pub open_patches: usize,
    pub open_issues: usize,
    pub patch: PatchDetailView,
}

pub async fn patches(
    Path(repo_name): Path<String>,
    State(state): State<Arc<AppState>>,
) -> Response {
    let (_entry, repo) = match open_repo(&state, &repo_name) {
        Ok(r) => r,
        Err(resp) => return resp,
    };

    let (open_patches, open_issues) = collab_counts(&repo);

    let all_patches = git_collab::state::list_patches_with_archived(&repo).unwrap_or_default();

    let patches = all_patches
        .into_iter()
        .map(|p| {
            let id = p.id.clone();
            let short_id = id[..8.min(id.len())].to_string();
            PatchListItem {
                short_id,
                id,
                status: p.status.as_str().to_string(),
                title: p.title,
                author: p.author.name,
                branch: p.branch,
                updated: p.last_updated,
            }
        })
        .collect();

    PatchesTemplate {
        site_title: state.site_title.clone(),
        repo_name,
        active_section: "patches".to_string(),
        open_patches,
        open_issues,
        patches,
    }
    .into_response()
}

pub async fn patch_detail(
    Path((repo_name, patch_id)): Path<(String, String)>,
    State(state): State<Arc<AppState>>,
) -> Response {
    let (_entry, repo) = match open_repo(&state, &repo_name) {
        Ok(r) => r,
        Err(resp) => return resp,
    };

    let (open_patches, open_issues) = collab_counts(&repo);

    let (ref_name, full_id) = match git_collab::state::resolve_patch_ref(&repo, &patch_id) {
        Ok(r) => r,
        Err(_) => return not_found(&state, format!("Patch '{}' not found.", patch_id)),
    };

    let ps = match git_collab::state::PatchState::from_ref(&repo, &ref_name, &full_id) {
        Ok(s) => s,
        Err(_) => return internal_error(&state, "Failed to load patch state."),
    };

    let patch = PatchDetailView {
        title: ps.title,
        body: ps.body,
        status: ps.status.as_str().to_string(),
        author: ps.author.name,
        branch: ps.branch,
        base_ref: ps.base_ref,
        revisions: ps.revisions.into_iter().map(|r| RevisionView {
            number: r.number,
            commit: r.commit,
            timestamp: r.timestamp,
            body: r.body,
        }).collect(),
        reviews: ps.reviews.into_iter().map(|r| ReviewView {
            author: r.author.name,
            verdict: r.verdict.as_str().to_string(),
            body: r.body,
            timestamp: r.timestamp,
            revision: r.revision,
        }).collect(),
        inline_comments: ps.inline_comments.into_iter().map(|ic| InlineCommentView {
            author: ic.author.name,
            file: ic.file,
            line: ic.line,
            body: ic.body,
            timestamp: ic.timestamp,
            revision: ic.revision,
        }).collect(),
        comments: ps.comments.into_iter().map(|c| CommentView {
            author: c.author.name,
            body: c.body,
            timestamp: c.timestamp,
        }).collect(),
    };

    PatchDetailTemplate {
        site_title: state.site_title.clone(),
        repo_name,
        active_section: "patches".to_string(),
        open_patches,
        open_issues,
        patch,
    }
    .into_response()
}