Improve logging during element scanning
This commit is contained in:
150
Sources/Generator/Processing/MetadataInfoLogger.swift
Normal file
150
Sources/Generator/Processing/MetadataInfoLogger.swift
Normal file
@ -0,0 +1,150 @@
|
||||
import Foundation
|
||||
|
||||
final class MetadataInfoLogger {
|
||||
|
||||
private let input: URL
|
||||
|
||||
private var numberOfMetadataFiles = 0
|
||||
|
||||
private var unusedProperties: [(name: String, source: String)] = []
|
||||
|
||||
private var invalidProperties: [(name: String, source: String, reason: String)] = []
|
||||
|
||||
private var unknownProperties: [(name: String, source: String)] = []
|
||||
|
||||
private var missingProperties: [(name: String, source: String)] = []
|
||||
|
||||
private var unreadableMetadata: [(source: String, error: Error)] = []
|
||||
|
||||
private var warnings: [(source: String, message: String)] = []
|
||||
|
||||
private var errors: [(source: String, message: String)] = []
|
||||
|
||||
init(input: URL) {
|
||||
self.input = input
|
||||
}
|
||||
|
||||
/**
|
||||
Adds an info message if a value is set for an unused property.
|
||||
- Note: Unused properties do not cause an element to be skipped.
|
||||
*/
|
||||
@discardableResult
|
||||
func unused<T>(_ value: Optional<T>, _ name: String, source: String) -> T where T: DefaultValueProvider {
|
||||
if let value {
|
||||
unusedProperties.append((name, source))
|
||||
return value
|
||||
}
|
||||
return T.defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
Cast a string value to another value, and using a default in case of errors.
|
||||
- Note: Invalid string values do not cause an element to be skipped.
|
||||
*/
|
||||
func cast<T>(_ value: String, _ name: String, source: String) -> T where T: DefaultValueProvider, T: StringProperty {
|
||||
guard let result = T.init(value) else {
|
||||
invalidProperties.append((name: name, source: source, reason: T.castFailureReason))
|
||||
return T.defaultValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
Cast a string value to another value, and using a default in case of errors or missing values.
|
||||
- Note: Invalid string values do not cause an element to be skipped.
|
||||
*/
|
||||
func cast<T>(_ value: String?, _ name: String, source: String) -> T where T: DefaultValueProvider, T: StringProperty {
|
||||
guard let value else {
|
||||
return T.defaultValue
|
||||
}
|
||||
guard let result = T.init(value) else {
|
||||
invalidProperties.append((name: name, source: source, reason: T.castFailureReason))
|
||||
return T.defaultValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
Cast the string value of an unused property to another value, and using a default in case of errors.
|
||||
- Note: Invalid string values do not cause an element to be skipped.
|
||||
*/
|
||||
func castUnused<R>(_ value: String?, _ name: String, source: String) -> R where R: DefaultValueProvider, R: StringProperty {
|
||||
unused(value.unwrapped { cast($0, name, source: source) }, name, source: source)
|
||||
}
|
||||
|
||||
/**
|
||||
Note an unknown property.
|
||||
- Note: Unknown properties do not cause an element to be skipped.
|
||||
*/
|
||||
func unknown(property: String, source: String) {
|
||||
unknownProperties.append((name: property, source: source))
|
||||
}
|
||||
|
||||
/**
|
||||
Ensure that a property is set, and aborting metadata decoding.
|
||||
- Note: Missing required properties cause an element to be skipped.
|
||||
*/
|
||||
func required<T>(_ value: T?, name: String, source: String, _ valid: inout Bool) -> T where T: DefaultValueProvider {
|
||||
guard let value = value else {
|
||||
missingProperties.append((name, source))
|
||||
valid = false
|
||||
return T.defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func warning(_ message: String, source: String) {
|
||||
warnings.append((source, message))
|
||||
}
|
||||
|
||||
func error(_ message: String, source: String) {
|
||||
errors.append((source, message))
|
||||
}
|
||||
|
||||
func failedToDecodeMetadata(source: String, error: Error) {
|
||||
unreadableMetadata.append((source, error))
|
||||
}
|
||||
|
||||
func readPotentialMetadata(atPath path: String, source: String) -> Data? {
|
||||
let url = input.appendingPathComponent(path)
|
||||
guard url.exists else {
|
||||
return nil
|
||||
}
|
||||
|
||||
numberOfMetadataFiles += 1
|
||||
printMetadataScanUpdate()
|
||||
do {
|
||||
return try Data(contentsOf: url)
|
||||
} catch {
|
||||
unreadableMetadata.append((source, error))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Printing
|
||||
|
||||
private func printMetadataScanUpdate() {
|
||||
print(String(format: "Scanning source files: %4d pages found \r", numberOfMetadataFiles), terminator: "")
|
||||
}
|
||||
|
||||
func printMetadataScanOverview() {
|
||||
print(" Number of pages: \(numberOfMetadataFiles)")
|
||||
print(" Unreadable files: \(unreadableMetadata.count)")
|
||||
print(" Unused properties: \(unusedProperties.count)")
|
||||
print(" Invalid properties: \(invalidProperties.count)")
|
||||
print(" Unknown properties: \(unknownProperties.count)")
|
||||
print(" Missing properties: \(missingProperties.count)")
|
||||
}
|
||||
|
||||
func writeResultsToFile(in folder: URL) throws {
|
||||
let url = folder.appendingPathComponent("Metadata issues.txt")
|
||||
var lines = ["Unreadable files:"] + unreadableMetadata.map { "\($0.source): \($0.error)" }
|
||||
lines += ["Unused properties:"] + unusedProperties.map { "\($0.source): \($0.name)" }
|
||||
lines += ["Invalid properties:"] + invalidProperties.map { "\($0.source): \($0.name) (\($0.reason))" }
|
||||
lines += ["Unknown properties:"] + unknownProperties.map { "\($0.source): \($0.name)" }
|
||||
lines += ["Missing properties:"] + missingProperties.map { "\($0.source): \($0.name)" }
|
||||
|
||||
let data = lines.joined(separator: "\n").data(using: .utf8)
|
||||
try data?.createFolderAndWrite(to: url)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user