Files
SwiftLint/Source/SwiftLintFramework/Reporters/HTMLReporter.swift
T
Danny Mösch a6c4fd98bc Move files from SwiftLintCore to SwiftLintFramework
Ideally, SwiftLintCore would some day only contain components
that are needed to define rules. Consequently, it would be the
only bundle required to import for (external) rule development.
2024-12-23 12:51:43 +01:00

169 lines
5.6 KiB
Swift

import Foundation
private let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
return formatter
}()
/// Reports violations as HTML.
struct HTMLReporter: Reporter {
// MARK: - Reporter Conformance
static let identifier = "html"
static let isRealtime = false
static let description = "Reports violations as HTML."
static func generateReport(_ violations: [StyleViolation]) -> String {
generateReport(violations, swiftlintVersion: Version.current.value, dateString: formatter.string(from: Date()))
}
// MARK: - Internal
// swiftlint:disable:next function_body_length
internal static func generateReport(_ violations: [StyleViolation],
swiftlintVersion: String,
dateString: String) -> String {
let rows = violations.enumerated()
.map { generateSingleRow(for: $1, at: $0 + 1) }
.joined(separator: "\n")
let fileCount = Set(violations.compactMap(\.location.file)).count
let warningCount = violations.filter({ $0.severity == .warning }).count
let errorCount = violations.filter({ $0.severity == .error }).count
return """
<!doctype html>
<html>
\t<head>
\t\t<meta charset="utf-8" />
\t\t<meta name="viewport" content="width=device-width, initial-scale=1.0" />
\t\t
\t\t<style type="text/css">
\t\t\tbody {
\t\t\t\tfont-family: Arial, Helvetica, sans-serif;
\t\t\t\tfont-size: 0.9rem;
\t\t\t}
\t\t\t
\t\t\ttable {
\t\t\t\tborder: 1px solid gray;
\t\t\t\tborder-collapse: collapse;
\t\t\t\t-moz-box-shadow: 3px 3px 4px #AAA;
\t\t\t\t-webkit-box-shadow: 3px 3px 4px #AAA;
\t\t\t\tbox-shadow: 3px 3px 4px #AAA;
\t\t\t\tvertical-align: top;
\t\t\t\theight: 64px;
\t\t\t}
\t\t\t
\t\t\ttd, th {
\t\t\t\tborder: 1px solid #D3D3D3;
\t\t\t\tpadding: 5px 10px 5px 10px;
\t\t\t}
\t\t\t
\t\t\tth {
\t\t\t\tborder-bottom: 1px solid gray;
\t\t\t\tbackground-color: rgba(41,52,92,0.313);
\t\t\t}
\t\t\t
\t\t\t.error, .warning {
\t\t\t\ttext-align: center;
\t\t\t}
\t\t\t
\t\t\t.error {
\t\t\t\tbackground-color: #FF9D92;
\t\t\t\tcolor: #7F0800;
\t\t\t}
\t\t\t
\t\t\t.warning {
\t\t\t\tbackground-color: #FFF59E;
\t\t\t\tcolor: #7F7000;
\t\t\t}
\t\t</style>
\t\t
\t\t<title>SwiftLint Report</title>
\t</head>
\t<body>
\t\t<h1>SwiftLint Report</h1>
\t\t
\t\t<hr />
\t\t
\t\t<h2>Violations</h2>
\t\t
\t\t<table>
\t\t\t<thead>
\t\t\t\t<tr>
\t\t\t\t\t<th style="width: 60pt;">
\t\t\t\t\t\t<b>Serial No.</b>
\t\t\t\t\t</th>
\t\t\t\t\t<th style="width: 500pt;">
\t\t\t\t\t\t<b>File</b>
\t\t\t\t\t</th>
\t\t\t\t\t<th style="width: 60pt;">
\t\t\t\t\t\t<b>Location</b>
\t\t\t\t\t</th>
\t\t\t\t\t<th style="width: 60pt;">
\t\t\t\t\t\t<b>Severity</b>
\t\t\t\t\t</th>
\t\t\t\t\t<th style="width: 500pt;">
\t\t\t\t\t\t<b>Message</b>
\t\t\t\t\t</th>
\t\t\t\t</tr>
\t\t\t</thead>
\t\t\t<tbody>
\(rows)
\t\t\t</tbody>
\t\t</table>
\t\t
\t\t<br/>
\t\t
\t\t<h2>Summary</h2>
\t\t
\t\t<table>
\t\t\t<tbody>
\t\t\t\t<tr>
\t\t\t\t\t<td>Total files with violations</td>
\t\t\t\t\t<td>\(fileCount)</td>
\t\t\t\t</tr>
\t\t\t\t<tr>
\t\t\t\t\t<td>Total warnings</td>
\t\t\t\t\t<td>\(warningCount)</td>
\t\t\t\t</tr>
\t\t\t\t<tr>
\t\t\t\t\t<td>Total errors</td>
\t\t\t\t\t<td>\(errorCount)</td>
\t\t\t\t</tr>
\t\t\t</tbody>
\t\t</table>
\t\t
\t\t<hr />
\t\t
\t\t<p>
\t\t\tCreated with
\t\t\t<a href="https://github.com/realm/SwiftLint"><b>SwiftLint</b></a>
\t\t\t\(swiftlintVersion) on \(dateString)
\t\t</p>
\t</body>
</html>
"""
}
// MARK: - Private
private static func generateSingleRow(for violation: StyleViolation, at index: Int) -> String {
let severity: String = violation.severity.rawValue.capitalized
let location = violation.location
let file: String = (violation.location.relativeFile ?? "<nopath>").escapedForXML()
let line: Int = location.line ?? 0
let character: Int = location.character ?? 0
return """
\t\t\t\t<tr>
\t\t\t\t\t<td style="text-align: right;">\(index)</td>
\t\t\t\t\t<td>\(file)</td>
\t\t\t\t\t<td style="text-align: center;">\(line):\(character)</td>
\t\t\t\t\t<td class="\(severity.lowercased())">\(severity)</td>
\t\t\t\t\t<td>\(violation.reason.escapedForXML())</td>
\t\t\t\t</tr>
"""
}
}