import Foundation private let formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short return formatter }() /// Reports violations as HTML. public struct HTMLReporter: Reporter { // MARK: - Reporter Conformance public static let identifier = "html" public static let isRealtime = false public var description: String { return "Reports violations as HTML." } public static func generateReport(_ violations: [StyleViolation]) -> String { return 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().reduce(into: "") { rows, indexAndViolation in rows.append(generateSingleRow(for: indexAndViolation.1, at: indexAndViolation.0 + 1)) } let fileCount = Set(violations.compactMap({ $0.location.file })).count let warningCount = violations.filter({ $0.severity == .warning }).count let errorCount = violations.filter({ $0.severity == .error }).count return [ "\n", "\n", "\t
\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\t| \n", "\t\t\t\t\t\tSerial No.\n", "\t\t\t\t\t | \n", "\t\t\t\t\t\n", "\t\t\t\t\t\tFile\n", "\t\t\t\t\t | \n", "\t\t\t\t\t\n", "\t\t\t\t\t\tLocation\n", "\t\t\t\t\t | \n", "\t\t\t\t\t\n", "\t\t\t\t\t\tSeverity\n", "\t\t\t\t\t | \n", "\t\t\t\t\t\n", "\t\t\t\t\t\tMessage\n", "\t\t\t\t\t | \n", "\t\t\t\t
|---|
| Total files with violations | \n", "\t\t\t\t\t\(fileCount) | \n", "\t\t\t\t
| Total warnings | \n", "\t\t\t\t\t\(warningCount) | \n", "\t\t\t\t
| Total errors | \n", "\t\t\t\t\t\(errorCount) | \n", "\t\t\t\t
\n", "\t\t\tCreated with\n", "\t\t\tSwiftLint\n", "\t\t\t", swiftlintVersion, " on ", dateString, "\n", "\t\t
\n", "\t\n", "" ].joined() } // 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 ?? "