GithubTableReportBuilder.kt
package com.sebastmar.module.report.internal.host.github
import com.sebastmar.module.report.info.Module
import com.sebastmar.module.report.info.PullRequest
import com.sebastmar.module.report.info.VersionedFile
import com.sebastmar.module.report.info.getDeletedLines
import com.sebastmar.module.report.info.getInsertedLines
import com.sebastmar.module.report.internal.ShouldLinkifyFiles
import com.sebastmar.module.report.internal.ShowCircleIndicators
import com.sebastmar.module.report.internal.ShowLineIndicators
import com.sebastmar.module.report.internal.TableReportBuilder
import com.sebastmar.module.report.internal.domain.GetPullRequest
import com.sebastmar.module.report.internal.domain.StringProvider
import com.sebastmar.module.report.internal.ext.table
import com.sebastmar.module.report.internal.ext.td
import com.sebastmar.module.report.internal.ext.th
import com.sebastmar.module.report.internal.ext.tr
import com.sebastmar.module.report.internal.system.SystemWrapper
/**
* A builder class for creating GitHub-specific table-based reports.
*
* This class is responsible for creating a structured HTML report designed for GitHub.
* The report includes sections such as a top warning for incorrect hosts, summaries of
* added, modified, and deleted files, and module-specific details presented in tabular format.
*
* The builder supports customizable options, such as displaying line indicators, linking files
* to their specific diff locations, and showing colored circle indicators for file status.
*
* @constructor Initializes the builder with dependencies and configuration options for the report.
* @param stringProvider Provides string resources for various report sections such as top and bottom sections.
* @param showLineIndicators Controls whether to show the count of added and deleted lines for files.
* @param shouldLinkifyFiles Determines whether files should be linked to their respective GitHub file diffs.
* @param showCircleIndicators Specifies whether to display colored circle indicators for file status
* (added, modified, deleted).
* @param systemWrapper Provides system-level details, such as the host platform and pull request's environment.
* @param builder A mutable string builder for composing the HTML content of the report.
* @param getPullRequest A function that retrieves the current pull request to generate the report for.
*/
@Suppress("LongParameterList")
internal class GithubTableReportBuilder(
private val stringProvider: StringProvider,
private val showLineIndicators: ShowLineIndicators,
private val shouldLinkifyFiles: ShouldLinkifyFiles,
private val showCircleIndicators: ShowCircleIndicators,
private val systemWrapper: SystemWrapper,
getPullRequest: GetPullRequest,
private val builder: StringBuilder = StringBuilder(),
) : TableReportBuilder(getPullRequest) {
override fun topSection(): Unit = with(builder) {
if (systemWrapper.onGithub().not()) {
appendLine(stringProvider.incorrectHostWarning())
}
val topSection = stringProvider.topSection()
if (topSection != null) {
appendLine(topSection)
}
}
override fun bottomSection() = with(builder) {
val bottomSection = stringProvider.bottomSection()
if (bottomSection != null) {
appendLine(bottomSection)
}
}
override fun table(block: () -> Unit): Unit = with(builder) {
table(block)
append("\n\n")
}
override fun headerRow() = with(builder) {
tr {
th()
if (pullRequest.createdFiles.isNotEmpty()) {
th {
append("Added")
if (showLineIndicators.value) {
val totalAdded = "+" + pullRequest.createdFiles.getInsertedLines()
append(" (${totalAdded.greenFlavor()})")
}
}
}
if (pullRequest.modifiedFiles.isNotEmpty()) {
th {
append("Modified")
if (showLineIndicators.value) {
val totalAdded = "+" + pullRequest.modifiedFiles.getInsertedLines()
val totalDeleted = "-" + pullRequest.modifiedFiles.getDeletedLines()
append(" (${totalAdded.greenFlavor()} / ${totalDeleted.redFlavor()})")
}
}
}
if (pullRequest.deletedFiles.isNotEmpty()) {
th {
append("Deleted")
if (showLineIndicators.value) {
val totalDeleted = "-" + pullRequest.deletedFiles.getDeletedLines()
append(" (${totalDeleted.redFlavor()})")
}
}
}
}
}
/**
* Writes the module information to the HTML report.
* Each module is displayed in a table row with its name and a list of created, modified, and removed files.
* Created files are marked with a green circle, modified files with a yellow circle,
* and removed files with a red circle.
* Each file is linked to its corresponding URL.
*/
override fun moduleRows() = with(builder) {
pullRequest.modules.sortedBy(Module::type).forEach { module ->
tr {
td {
append("<div style=\"display: inline-block;\"><b>${module.name}</b></div>")
}
if (pullRequest.createdFiles.isNotEmpty()) {
filesColumn(module.createdFiles, "🟢")
}
if (pullRequest.modifiedFiles.isNotEmpty()) {
filesColumn(module.modifiedFiles, "🟡")
}
if (pullRequest.deletedFiles.isNotEmpty()) {
filesColumn(module.deletedFiles, "🔴")
}
}
}
}
override fun getReport(): String = builder.toString()
private fun StringBuilder.filesColumn(
moduleFiles: List<VersionedFile>,
circleIndicator: String,
) {
td {
moduleFiles
.map { file ->
when (shouldLinkifyFiles.value) {
true -> pullRequest.getLinkOf(file)
false -> file.name
}
}
.forEach { fileName ->
if (showCircleIndicators.value) {
append("$circleIndicator ")
}
append("$fileName<br>")
}
}
}
/**
* Formats the string to be displayed in green color using LaTeX-like syntax.
* This syntax is compatible with Github markdown.
*/
private fun String.greenFlavor(): String = "\$\\color{Green}{\\textsf{$this}}\$"
/**
* Formats the string to be displayed in red color using LaTeX-like syntax.
* This syntax is compatible with Github markdown.
*/
private fun String.redFlavor() = "\$\\color{Red}{\\textsf{$this}}\$"
/**
* Generates an HTML hyperlink to the specific file diff on GitHub.
*
* The link is constructed using the pull request's HTML URL and the SHA-256 hash
* of the file's path, which is a format GitHub uses to identify specific file changes
* within a pull request's "Files changed" tab.
*
* @return An HTML `<a>` tag string that links to the file diff on GitHub.
* The link text will be the `name` of the file.
*/
private fun PullRequest.getLinkOf(file: VersionedFile): String {
return "<a href=\"$htmlLink/files#diff-${file.sha256Path}\">${file.name}</a>"
}
}