import Foundation struct RDEPUBBuildDiagnosticsReporter { func phase7SemanticSummary( title: String?, diagnostics: [RDEPUBTextChapterPaginationDiagnostic] ) -> String? { guard !diagnostics.isEmpty else { return nil } let blockKinds = uniqueValues(from: diagnostics.flatMap(\.blockKinds)) let semanticHints = uniqueValues(from: diagnostics.flatMap(\.semanticHints)) let attachmentPlacements = uniqueValues(from: diagnostics.flatMap(\.attachmentPlacements)) let note = diagnostics .flatMap(\.sampleNotes) .first(where: { $0.contains("semantic") || $0.contains("attachment") || $0.contains("block kinds") }) var parts = [ title, "章节 \(diagnostics.count)", blockKinds.isEmpty ? nil : "block kinds [\(blockKinds.map(\.rawValue).joined(separator: ","))]", semanticHints.isEmpty ? nil : "hints [\(semanticHints.map(\.rawValue).joined(separator: ","))]", attachmentPlacements.isEmpty ? nil : "placements [\(attachmentPlacements.map(\.rawValue).joined(separator: ","))]" ].compactMap { $0 } if let note { parts.append(note) } return parts.joined(separator: " · ") } func chapterDiagnostic( href: String, title: String, pages: [RDEPUBTextPage] ) -> RDEPUBTextChapterPaginationDiagnostic { RDEPUBTextChapterPaginationDiagnostic( href: href, title: title, pageCount: pages.count, breakReasons: pages.map(\.metadata.breakReason), attachmentPageCount: pages.filter { !$0.metadata.attachmentKinds.isEmpty }.count, blockAdjustedPageCount: pages.filter { $0.metadata.breakReason == .blockBoundary || $0.metadata.breakReason == .attachmentBoundary }.count, blockKinds: uniqueValues(from: pages.flatMap(\.metadata.blockKinds)), semanticHints: uniqueValues(from: pages.flatMap(\.metadata.semanticHints)), attachmentPlacements: uniqueValues(from: pages.flatMap(\.metadata.attachmentPlacements)), sampleNotes: Array(pages.flatMap(\.metadata.diagnostics).prefix(4)) ) } private func uniqueValues(from values: [T]) -> [T] { values.reduce(into: [T]()) { result, value in if !result.contains(value) { result.append(value) } } } }