2022-08-19 18:05:06 +02:00
|
|
|
|
import Foundation
|
|
|
|
|
|
2022-08-26 17:40:51 +02:00
|
|
|
|
final class ValidationLog {
|
2022-08-19 18:05:06 +02:00
|
|
|
|
|
2022-08-25 00:09:39 +02:00
|
|
|
|
private enum LogLevel: String {
|
|
|
|
|
case error = "ERROR"
|
|
|
|
|
case warning = "WARNING"
|
|
|
|
|
case info = "INFO"
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 18:05:06 +02:00
|
|
|
|
init() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 00:09:39 +02:00
|
|
|
|
private func add(_ type: LogLevel, item: ContentError) {
|
|
|
|
|
let errorText: String
|
|
|
|
|
if let err = item.error {
|
|
|
|
|
errorText = ", Error: \(err.localizedDescription)"
|
|
|
|
|
} else {
|
|
|
|
|
errorText = ""
|
|
|
|
|
}
|
2022-08-29 19:20:13 +02:00
|
|
|
|
print("[\(type.rawValue)][\(item.source)] \(item.reason)\(errorText)")
|
2022-08-25 00:09:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 18:05:06 +02:00
|
|
|
|
func add(error: ContentError) {
|
2022-08-25 00:09:39 +02:00
|
|
|
|
add(.error, item: error)
|
2022-08-19 18:05:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func add(error reason: String, source: String, error: Error? = nil) {
|
|
|
|
|
add(error: .init(reason: reason, source: source, error: error))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func add(warning: ContentError) {
|
2022-08-25 00:09:39 +02:00
|
|
|
|
add(.warning, item: warning)
|
2022-08-19 18:05:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func add(warning reason: String, source: String, error: Error? = nil) {
|
|
|
|
|
add(warning: .init(reason: reason, source: source, error: error))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func add(info: ContentError) {
|
2022-08-25 00:09:39 +02:00
|
|
|
|
add(.info, item: info)
|
2022-08-19 18:05:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func add(info reason: String, source: String, error: Error? = nil) {
|
|
|
|
|
add(info: .init(reason: reason, source: source, error: error))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult
|
|
|
|
|
func unused<T, R>(_ value: Optional<T>, _ name: String, source: String) -> Optional<R> {
|
|
|
|
|
if value != nil {
|
|
|
|
|
add(info: "Unused property '\(name)'", source: source)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unknown(property: String, source: String) {
|
|
|
|
|
add(info: "Unknown property '\(property)'", source: source)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func required<T>(_ value: Optional<T>, name: String, source: String) -> Optional<T> {
|
|
|
|
|
guard let value = value else {
|
|
|
|
|
add(error: "Missing property '\(name)'", source: source)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unexpected<T>(_ value: Optional<T>, name: String, source: String) -> Optional<T> {
|
|
|
|
|
if let value = value {
|
|
|
|
|
add(error: "Unexpected property '\(name)' = '\(value)'", source: source)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func missing(_ file: String, requiredBy source: String) {
|
|
|
|
|
print("[ERROR] Missing file '\(file)' required by \(source)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func failedToOpen(_ file: String, requiredBy source: String, error: Error?) {
|
|
|
|
|
print("[ERROR] Failed to open file '\(file)' required by \(source): \(error?.localizedDescription ?? "No error provided")")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func state(_ raw: String?, source: String) -> PageState {
|
|
|
|
|
guard let raw = raw else {
|
|
|
|
|
return .standard
|
|
|
|
|
}
|
|
|
|
|
guard let state = PageState(rawValue: raw) else {
|
|
|
|
|
add(warning: "Invalid 'state' '\(raw)', using 'standard'", source: source)
|
|
|
|
|
return .standard
|
|
|
|
|
}
|
|
|
|
|
return state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func thumbnailStyle(_ raw: String?, source: String) -> ThumbnailStyle {
|
|
|
|
|
guard let raw = raw else {
|
|
|
|
|
return .large
|
|
|
|
|
}
|
|
|
|
|
guard let style = ThumbnailStyle(rawValue: raw) else {
|
|
|
|
|
add(warning: "Invalid 'thumbnailStyle' '\(raw)', using 'large'", source: source)
|
|
|
|
|
return .large
|
|
|
|
|
}
|
|
|
|
|
return style
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func linkPreviewThumbnail(customFile: String?, for language: String, in folder: URL, source: String) -> String? {
|
|
|
|
|
if let customFile = customFile {
|
|
|
|
|
#warning("Allow absolute urls for link preview thumbnails")
|
|
|
|
|
let customFileUrl = folder.appendingPathComponent(customFile)
|
|
|
|
|
guard customFileUrl.exists else {
|
|
|
|
|
missing(customFile, requiredBy: "property 'linkPreviewImage' in metadata of \(source)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return customFile
|
|
|
|
|
}
|
|
|
|
|
guard let thumbnail = Element.findThumbnail(for: language, in: folder) else {
|
|
|
|
|
// Link preview images are not necessarily required
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return thumbnail
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func moreLinkText(_ elementText: String?, parent parentText: String?, source: String) -> String {
|
|
|
|
|
if let elementText = elementText {
|
|
|
|
|
return elementText
|
|
|
|
|
}
|
|
|
|
|
let standard = Element.LocalizedMetadata.moreLinkDefaultText
|
|
|
|
|
guard let parentText = parentText, parentText != standard else {
|
|
|
|
|
add(error: "Missing property 'moreLinkText'", source: source)
|
|
|
|
|
return standard
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parentText
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func date(from string: String?, property: String, source: String) -> Date? {
|
|
|
|
|
guard let string = string else {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2022-08-26 17:40:51 +02:00
|
|
|
|
guard let date = ValidationLog.metadataDate.date(from: string) else {
|
2022-08-19 18:05:06 +02:00
|
|
|
|
add(warning: "Invalid date string '\(string)' for property '\(property)'", source: source)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return date
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static let metadataDate: DateFormatter = {
|
|
|
|
|
let df = DateFormatter()
|
|
|
|
|
df.dateFormat = "dd.MM.yy"
|
|
|
|
|
return df
|
|
|
|
|
}()
|
|
|
|
|
}
|