import Foundation public struct StorageAuditReport: Sendable { public static func items(for catalog: C.Type) -> [StorageKeyDescriptor] { catalog.allKeys.map(\.descriptor) } public static func renderText(for catalog: C.Type) -> String { renderText(items(for: catalog)) } public static func renderText(_ entries: [AnyStorageKey]) -> String { renderText(entries.map(\.descriptor)) } public static func renderGlobalRegistry() async -> String { let entries = await StorageRouter.shared.allRegisteredEntries() return renderText(entries) } public static func renderGlobalRegistryGrouped() async -> String { let catalogs = await StorageRouter.shared.allRegisteredCatalogs() var reportLines: [String] = [] for catalogName in catalogs.keys.sorted() { reportLines.append("=== \(catalogName) ===") if let entries = catalogs[catalogName] { let keysReport = renderText(entries) reportLines.append(keysReport) } reportLines.append("") } return reportLines.joined(separator: "\n") } public static func renderText(_ items: [StorageKeyDescriptor]) -> String { let lines = items.map { item in var parts: [String] = [] parts.append("name=\(item.name)") if let catalog = item.catalog { parts.append("catalog=\(catalog)") } parts.append("domain=\(string(for: item.domain))") parts.append("security=\(string(for: item.security))") parts.append("serializer=\(item.serializer)") parts.append("value=\(item.valueType)") parts.append("owner=\(item.owner)") parts.append("availability=\(string(for: item.availability))") parts.append("sync=\(string(for: item.syncPolicy))") parts.append("description=\(item.description)") return parts.joined(separator: " | ") } return lines.joined(separator: "\n") } private static func string(for domain: StorageDomain) -> String { switch domain { case .userDefaults(let suite): return "userDefaults(\(suite ?? "standard"))" case .appGroupUserDefaults(let identifier): return "appGroupUserDefaults(\(identifier ?? "default"))" case .keychain(let service): return "keychain(\(service ?? "default"))" case .fileSystem(let directory): return "fileSystem(\(string(for: directory)))" case .encryptedFileSystem(let directory): return "encryptedFileSystem(\(string(for: directory)))" case .appGroupFileSystem(let identifier, let directory): return "appGroupFileSystem(\(identifier ?? "default"), \(string(for: directory)))" } } private static func string(for directory: FileDirectory) -> String { switch directory { case .documents: return "documents" case .caches: return "caches" case .custom(let url): return "custom(\(url.path))" } } private static func string(for availability: PlatformAvailability) -> String { switch availability { case .all: return "all" case .phoneOnly: return "phoneOnly" case .watchOnly: return "watchOnly" case .phoneWithWatchSync: return "phoneWithWatchSync" } } private static func string(for syncPolicy: SyncPolicy) -> String { switch syncPolicy { case .never: return "never" case .manual: return "manual" case .automaticSmall: return "automaticSmall" } } private static func string(for security: SecurityPolicy) -> String { switch security { case .none: return "none" case .encrypted(let policy): return "encrypted(\(string(for: policy)))" case .keychain(let accessibility, let accessControl): let accessControlValue = accessControl?.displayName ?? "none" return "keychain(\(accessibility.displayName), \(accessControlValue))" } } private static func string(for policy: SecurityPolicy.EncryptionPolicy) -> String { switch policy { case .aes256(let derivation): return "aes256(\(string(for: derivation)))" case .chacha20Poly1305(let derivation): return "chacha20Poly1305(\(string(for: derivation)))" case .external(let source, let derivation): return "external(\(source.id), \(string(for: derivation)))" } } private static func string(for derivation: SecurityPolicy.KeyDerivation) -> String { switch derivation { case .pbkdf2(let iterations, _): return "pbkdf2(\(iterations ?? 0))" case .hkdf: return "hkdf" } } }