89 lines
2.8 KiB
Swift
89 lines
2.8 KiB
Swift
import Dependencies
|
|
import DependenciesMacros
|
|
import FileClient
|
|
import Foundation
|
|
|
|
public extension DependencyValues {
|
|
|
|
/// Perform operations with configuration files.
|
|
var configurationClient: ConfigurationClient {
|
|
get { self[ConfigurationClient.self] }
|
|
set { self[ConfigurationClient.self] = newValue }
|
|
}
|
|
}
|
|
|
|
/// Handles interactions with configuration files.
|
|
@DependencyClient
|
|
public struct ConfigurationClient: Sendable {
|
|
|
|
/// Find a configuration file in the given directory or in current working directory.
|
|
public var find: @Sendable (URL?) async throws -> ConfigurationFile?
|
|
|
|
/// Load a configuration file.
|
|
public var load: @Sendable (ConfigurationFile) async throws -> Configuration
|
|
|
|
/// Write a configuration file.
|
|
public var write: @Sendable (Configuration, ConfigurationFile) async throws -> Void
|
|
|
|
/// Find a configuration file and load it if found.
|
|
public func findAndLoad(_ url: URL? = nil) async throws -> Configuration {
|
|
guard let url = try await find(url) else {
|
|
throw ConfigurationClientError.configurationNotFound
|
|
}
|
|
return try await load(url)
|
|
}
|
|
}
|
|
|
|
extension ConfigurationClient: DependencyKey {
|
|
public static let testValue: ConfigurationClient = Self()
|
|
|
|
public static var liveValue: ConfigurationClient {
|
|
.init(
|
|
find: { try await findConfiguration($0) },
|
|
load: { try await $0.load() ?? .mock },
|
|
write: { try await $1.write($0) }
|
|
)
|
|
}
|
|
}
|
|
|
|
private func findConfiguration(_ url: URL?) async throws -> ConfigurationFile? {
|
|
@Dependency(\.fileClient) var fileClient
|
|
|
|
var url: URL! = url
|
|
if url == nil {
|
|
url = try await URL(filePath: fileClient.currentDirectory())
|
|
}
|
|
|
|
// Check if url is a valid configuration url.
|
|
var configurationFile = ConfigurationFile(url: url)
|
|
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
return configurationFile
|
|
}
|
|
|
|
guard try await fileClient.isDirectory(url.cleanFilePath) else {
|
|
throw ConfigurationClientError.invalidConfigurationDirectory(path: url.cleanFilePath)
|
|
}
|
|
|
|
// Check for toml file.
|
|
let tomlUrl = url.appending(path: "\(ConfigurationClient.Constants.defaultFileNameWithoutExtension).toml")
|
|
configurationFile = ConfigurationFile(url: tomlUrl)
|
|
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
return configurationFile
|
|
}
|
|
|
|
// Check for json file.
|
|
let jsonUrl = url.appending(path: "\(ConfigurationClient.Constants.defaultFileNameWithoutExtension).json")
|
|
configurationFile = ConfigurationFile(url: jsonUrl)
|
|
if let configurationFile, fileClient.fileExists(configurationFile.url) {
|
|
return configurationFile
|
|
}
|
|
|
|
// Couldn't find valid configuration file.
|
|
return nil
|
|
}
|
|
|
|
enum ConfigurationClientError: Error {
|
|
case configurationNotFound
|
|
case invalidConfigurationDirectory(path: String)
|
|
}
|