LocalData/Sources/LocalData/Audit/StorageAuditReport.swift
Matt Bruce 8c8d4a0db8 Update Audit, Models, Services + tests + docs
Summary:
- Sources: Audit, Models, Services
- Tests: ModularRegistryTests.swift
- Docs: Proposal, README
- Added symbols: func withCatalog, func allRegisteredCatalogs, func allRegisteredEntries, struct TestRegistryKey, typealias Value, struct CatalogA (+3 more)

Stats:
- 7 files changed, 229 insertions(+), 18 deletions(-)
2026-01-18 14:53:29 -06:00

141 lines
5.0 KiB
Swift

import Foundation
public struct StorageAuditReport: Sendable {
public static func items<C: StorageKeyCatalog>(for catalog: C.Type) -> [StorageKeyDescriptor] {
catalog.allKeys.map(\.descriptor)
}
public static func renderText<C: StorageKeyCatalog>(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"
}
}
}