package review import ( "fmt" "strings" ) // FormatMarkdown formats a ReviewResult into the markdown body for a Gitea review. func FormatMarkdown(result *ReviewResult, reviewerName string) string { var sb strings.Builder if reviewerName != "" { title := strings.ToUpper(reviewerName[:1]) + reviewerName[1:] sb.WriteString(fmt.Sprintf("# %s Review\n\n", title)) } sb.WriteString("## Summary\n\n") sb.WriteString(result.Summary) sb.WriteString("\n\n") if len(result.Findings) > 0 { sb.WriteString("## Findings\n\n") sb.WriteString("| # | Severity | File | Line | Finding |\n") sb.WriteString("|---|----------|------|------|--------|\n") for i, f := range result.Findings { sb.WriteString(fmt.Sprintf("| %d | [%s] | `%s` | %d | %s |\n", i+1, f.Severity, f.File, f.Line, f.Finding)) } sb.WriteString("\n") } sb.WriteString("## Recommendation\n\n") sb.WriteString(fmt.Sprintf("**%s** — %s\n", result.Verdict, result.Recommendation)) if reviewerName != "" { sb.WriteString(fmt.Sprintf("\n---\n*Review by %s*\n", reviewerName)) // Hidden sentinel for identifying this bot's reviews during cleanup sb.WriteString(fmt.Sprintf("\n\n", reviewerName)) } return sb.String() } // GiteaEvent converts the verdict to the Gitea API event string. func GiteaEvent(verdict string) string { switch verdict { case "APPROVE": return "APPROVED" case "REQUEST_CHANGES": return "REQUEST_CHANGES" default: return "COMMENT" } } // FormatMarkdownWithDisplay formats a ReviewResult with separate display name and sentinel name. // displayName is used for the header title, sentinelName is used for the cleanup sentinel. // If displayName is empty, sentinelName is used for both. func FormatMarkdownWithDisplay(result *ReviewResult, displayName, sentinelName string) string { var sb strings.Builder // Use display name for header, or fall back to sentinel name headerName := displayName if headerName == "" { headerName = sentinelName } if headerName != "" { title := strings.ToUpper(headerName[:1]) + headerName[1:] sb.WriteString(fmt.Sprintf("# %s Review\n\n", title)) } sb.WriteString("## Summary\n\n") sb.WriteString(result.Summary) sb.WriteString("\n\n") if len(result.Findings) > 0 { sb.WriteString("## Findings\n\n") sb.WriteString("| # | Severity | File | Line | Finding |\n") sb.WriteString("|---|----------|------|------|--------|\n") for i, f := range result.Findings { sb.WriteString(fmt.Sprintf("| %d | [%s] | `%s` | %d | %s |\n", i+1, f.Severity, f.File, f.Line, f.Finding)) } sb.WriteString("\n") } sb.WriteString("## Recommendation\n\n") sb.WriteString(fmt.Sprintf("**%s** — %s\n", result.Verdict, result.Recommendation)) if sentinelName != "" { sb.WriteString(fmt.Sprintf("\n---\n*Review by %s*\n", headerName)) // Hidden sentinel for identifying this bot's reviews during cleanup sb.WriteString(fmt.Sprintf("\n\n", sentinelName)) } return sb.String() }