import Foundation /// Reads text files of unknown provenance. /// /// `.vcf` and `.ics` files reaching the app may have come from any address book /// or calendar going back twenty years, so the declared encoding — if there is /// one — cannot be trusted. Candidates are tried in order of likelihood. public enum TextFile { public static func read(contentsOf url: URL) throws -> String { let data = try Data(contentsOf: url) return decode(data) } static func decode(_ data: Data) -> String { // A UTF-8 BOM would otherwise survive into the first property name. var bytes = data if bytes.starts(with: [0xEF, 0xBB, 0xBF]) { bytes = bytes.dropFirst(3) } for encoding in [String.Encoding.utf8, .windowsCP1252, .isoLatin1] { if let text = String(data: bytes, encoding: encoding) { return text } } return String(decoding: bytes, as: UTF8.self) } } /// One output file produced by sanitizing. public struct SanitizedFile: Sendable, Equatable { /// Base name without extension; the caller allocates the final filename. public var suggestedName: String public var contents: String public init(suggestedName: String, contents: String) { self.suggestedName = suggestedName self.contents = contents } } /// The outcome of sanitizing one input file. public struct FileSanitizationResult: Sendable, Equatable { public var files: [SanitizedFile] public var warnings: [String] public init(files: [SanitizedFile], warnings: [String] = []) { self.files = files self.warnings = warnings } }