swift
复制代码
import Foundation
import STFilePath
/// 表示一个可下载文件的协议,支持异步获取和保存数据。
public protocol DownloadableFile: Sendable {
/// 文件对应的数据模型类型
associatedtype Model: Sendable
/// 获取操作的数据转换方法:从文件模型转换为目标类型
typealias FetchTransform<To: Sendable> = @Sendable (_ model: Model) async throws -> To
/// 保存操作的数据转换方法:从目标类型转换为文件模型
typealias SaveTransform<To: Sendable> = @Sendable (_ model: To) async throws -> Model
/// 异步获取文件内容
func fetch() async throws -> Model
/// 异步保存文件内容
func save(_ model: Model?) async throws
}
public extension DownloadableFile {
/// 基于当前文件定义一个新的映射关系(转换模型类型)
func map<To: Sendable>(
fetch: @escaping FetchTransform<To>,
save: @escaping SaveTransform<To>
) -> DKFileMap<Self, To> {
DKFileMap(file: self, fetch: fetch, save: save)
}
/// 为 Data 类型文件添加压缩/解压逻辑
func compression(_ algorithm: STComparator.Algorithm) -> DKFileMap<Self, Data> where Model == Data {
self.map { model in
try STComparator.decompress(model, algorithm: algorithm)
} save: { model in
try STComparator.compress(model, algorithm: algorithm)
}
}
/// 为 Data 类型文件添加 Codable 支持(自定义编码器与解码器)
func codable<Kind: Codable>(
_ kind: Kind.Type,
encoder: JSONEncoder,
decoder: JSONDecoder
) -> DKFileMap<Self, Kind> where Model == Data {
self.map { model in
try decoder.decode(kind.self, from: model)
} save: { model in
try encoder.encode(model)
}
}
/// 为 Data 类型文件添加默认的 Codable 支持(自动配置编码器/解码器)
func codable<Kind: Codable>(
_ kind: Kind.Type,
encoderOutputFormatting: JSONEncoder.OutputFormatting = [.prettyPrinted, .sortedKeys]
) -> DKFileMap<Self, Kind> where Model == Data {
let encoder = JSONEncoder()
encoder.dataEncodingStrategy = .base64
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = encoderOutputFormatting
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .base64
decoder.dateDecodingStrategy = .iso8601
return self.codable(kind, encoder: encoder, decoder: decoder)
}
}
/// 基于通用文件接口实现 DownloadableFile 的 Data 类型封装
public struct DFDataFile: Sendable, DownloadableFile {
public var file: any AnyFile
public init(file: any AnyFile) {
self.file = file
}
/// 快捷构造器:通过文件夹与文件名创建 Data 文件
public init(folder: any AnyFolder, name: String) {
self.init(file: folder.file(name))
}
/// 异步获取文件数据
public func fetch() async throws -> Data {
try await file.data()
}
/// 异步保存文件数据
public func save(_ model: Data?) async throws {
try await file.overlay(with: model, options: .init())
}
}
/// 用于将 DownloadableFile 进行类型映射的通用结构体
public struct DKFileMap<DKFile: DownloadableFile, To: Sendable>: Sendable, DownloadableFile {
public typealias Model = To
public let file: DKFile
public let fetchTransform: DKFile.FetchTransform<To>
public let saveTransform: DKFile.SaveTransform<To>
public init(
file: DKFile,
fetch: @escaping DKFile.FetchTransform<To>,
save: @escaping DKFile.SaveTransform<To>
) {
self.file = file
self.fetchTransform = fetch
self.saveTransform = save
}
/// 获取转换后的模型数据
public func fetch() async throws -> To {
let data = try await file.fetch()
return try await fetchTransform(data)
}
/// 保存转换后的模型数据
public func save(_ model: To?) async throws {
if let model = model {
let data = try await saveTransform(model)
try await file.save(data)
} else {
try await file.save(nil)
}
}
}