🐸 Frogue(Fed)

~ololduck/Frogue

Raw

use actix_web::HttpResponse;
use maud::{Markup, Render, html};

use crate::repositories::git;
use crate::views::{
    Page, RefSummary,
    commit::{diff_stats_summary, file_diffs},
    commits::CommitLine,
    components::{Complaint, RefSelect, RepositoryChrome, RepositoryTab},
    repositories::CommitSummary,
};

/// The body of the compare page, showing the diff between two revisions.
pub(super) struct CompareBody<'a> {
    pub(super) owner: &'a str,
    pub(super) slug: &'a str,
    pub(super) visibility: &'a str,
    pub(super) default_branch: &'a str,
    pub(super) base: &'a str,
    pub(super) head: &'a str,
    pub(super) commits: &'a [CommitSummary],
    pub(super) files: &'a [git::DiffFile],
    pub(super) viewer: Option<&'a str>,
}

impl Render for CompareBody<'_> {
    fn render(&self) -> Markup {
        html! {
            (RepositoryChrome {
                owner: self.owner,
                slug: self.slug,
                visibility: self.visibility,
                default_branch: self.default_branch,
                current_tab: RepositoryTab::Compare,
                is_owner: self.viewer == Some(self.owner),
            })
            div class="compare-header" {
                p {
                    "Comparing "
                    a href=(format!("/~{}/{}/commits/{}", self.owner, self.slug, self.base)) {
                        code { (self.base) }
                    }
                    " ... "
                    a href=(format!("/~{}/{}/commits/{}", self.owner, self.slug, self.head)) {
                        code { (self.head) }
                    }
                }
            }
            @if !self.commits.is_empty() {
                h3 { "Commits (" (self.commits.len()) ")" }
                ul class="commits" {
                    @for commit in self.commits {
                        li {
                            (CommitLine {
                                owner: self.owner,
                                slug: self.slug,
                                commit,
                            })
                        }
                    }
                }
            }
            @if !self.files.is_empty() {
                div class="commit-stats" {
                    (diff_stats_summary(self.files))
                }
                div class="commit-diff" {
                    (file_diffs(self.files))
                }
            } @else {
                p class="empty" {
                    "No differences between " (self.base) " and " (self.head) "."
                }
            }
        }
    }
}

/// The body of the compare landing page, with a form to pick two revisions.
pub(super) struct CompareFormBody<'a> {
    pub(super) owner: &'a str,
    pub(super) slug: &'a str,
    pub(super) visibility: &'static str,
    pub(super) default_branch: &'a str,
    pub(super) branches: &'a [RefSummary],
    pub(super) tags: &'a [RefSummary],
    pub(super) patches: &'a [PatchSummary],
    pub(super) error: Option<&'a str>,
    pub(super) viewer: Option<&'a str>,
}

/// A patch proposal for the compare form.
pub struct PatchSummary {
    pub number: i64,
    pub summary: String,
}

impl Render for CompareFormBody<'_> {
    fn render(&self) -> Markup {
        html! {
            (RepositoryChrome {
                owner: self.owner,
                slug: self.slug,
                visibility: self.visibility,
                default_branch: self.default_branch,
                current_tab: RepositoryTab::Compare,
                is_owner: self.viewer == Some(self.owner),
            })
            (Complaint(self.error))
            @if self.branches.is_empty() && self.tags.is_empty() && self.patches.is_empty() {
                p class="empty" { "Nothing to compare yet." }
            } @else {
                form method="get" action=(format!("/~{}/{}/compare", self.owner, self.slug)) class="compare-form" {
                    (RefSelect {
                        name: "from",
                        label: "Compare",
                        branches: self.branches,
                        tags: self.tags,
                        patches: self.patches,
                        selected: self.default_branch,
                    })
                    span class="dots" { "..." }
                    (RefSelect {
                        name: "to",
                        label: "with",
                        branches: self.branches,
                        tags: self.tags,
                        patches: self.patches,
                        selected: self.default_branch,
                    })
                    button type="submit" { "Compare" }
                }
            }
        }
    }
}

/// The compare page showing the diff between two revisions.
pub fn compare(
    viewer: Option<&str>,
    owner: &str,
    slug: &str,
    visibility: &'static str,
    default_branch: &str,
    base: &str,
    head: &str,
    commits: &[CommitSummary],
    files: &[git::DiffFile],
) -> HttpResponse {
    Page {
        title: format!("~{owner}/{slug}: Compare {base}...{head} - Frogue"),
        viewer: viewer.map(str::to_string),
        extra_head: None,
        body: CompareBody {
            owner,
            slug,
            visibility,
            default_branch,
            base,
            head,
            commits,
            files,
            viewer,
        },
    }
    .into_response()
}

/// The compare landing page with a form to pick two revisions.
pub fn compare_form(
    viewer: Option<&str>,
    owner: &str,
    slug: &str,
    visibility: &'static str,
    default_branch: &str,
    branches: &[RefSummary],
    tags: &[RefSummary],
    patches: &[PatchSummary],
    error: Option<&str>,
) -> HttpResponse {
    Page {
        title: format!("~{owner}/{slug}: Compare - Frogue"),
        viewer: viewer.map(str::to_string),
        extra_head: None,
        body: CompareFormBody {
            owner,
            slug,
            visibility,
            default_branch,
            branches,
            tags,
            patches,
            error,
            viewer,
        },
    }
    .into_response()
}