This commit is contained in:
@@ -14,25 +14,38 @@ public extension DependencyValues {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the interface needed for the command line application.
|
||||
///
|
||||
///
|
||||
@DependencyClient
|
||||
public struct CliClient {
|
||||
|
||||
/// Parse a log level from the given `EnvVars`.
|
||||
public var logLevel: @Sendable (EnvVars) -> Logger.Level = { _ in .debug }
|
||||
|
||||
/// Generate the `EnvVars` with the given parameters.
|
||||
public var makeEnvVars: @Sendable (EnvVarsRequest) async throws -> EnvVars
|
||||
|
||||
/// Generate the `MQTTClient` with the given parameters.
|
||||
public var makeClient: @Sendable (ClientRequest) throws -> MQTTClient
|
||||
|
||||
/// Attempt to parse a string to an `MQTTClient.Version`.
|
||||
public var parseMqttClientVersion: @Sendable (String) -> MQTTClient.Version?
|
||||
|
||||
/// Represents the parameters needed to create an `MQTTClient`.
|
||||
///
|
||||
///
|
||||
public struct ClientRequest: Sendable {
|
||||
public let envVars: EnvVars
|
||||
public let environment: EnvVars
|
||||
public let eventLoopGroup: MultiThreadedEventLoopGroup
|
||||
public let logger: Logger?
|
||||
|
||||
public init(
|
||||
envVars: EnvVars,
|
||||
environment: EnvVars,
|
||||
eventLoopGroup: MultiThreadedEventLoopGroup,
|
||||
logger: Logger?
|
||||
) {
|
||||
self.envVars = envVars
|
||||
self.environment = environment
|
||||
self.eventLoopGroup = eventLoopGroup
|
||||
self.logger = logger
|
||||
}
|
||||
@@ -63,7 +76,7 @@ extension CliClient: DependencyKey {
|
||||
Self(
|
||||
logLevel: { Logger.Level.from(environment: $0) },
|
||||
makeEnvVars: {
|
||||
try EnvVars.load(
|
||||
try await EnvVars.load(
|
||||
dotEnvFile: $0.envFilePath,
|
||||
logger: $0.logger,
|
||||
version: $0.mqttClientVersion
|
||||
@@ -71,7 +84,7 @@ extension CliClient: DependencyKey {
|
||||
},
|
||||
makeClient: {
|
||||
MQTTClient(
|
||||
envVars: $0.envVars,
|
||||
environment: $0.environment,
|
||||
eventLoopGroup: $0.eventLoopGroup,
|
||||
logger: $0.logger
|
||||
)
|
||||
@@ -81,32 +94,44 @@ extension CliClient: DependencyKey {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
extension EnvironmentDependency {
|
||||
func dotEnvDict(path: String?) async throws -> [String: String] {
|
||||
guard let path,
|
||||
let file = FileType(path: path)
|
||||
else { return [:] }
|
||||
return try await load(file)
|
||||
}
|
||||
}
|
||||
|
||||
extension EnvVars {
|
||||
|
||||
/// Load the `EnvVars` from the environment.
|
||||
///
|
||||
/// - Paramaters:
|
||||
/// - dotEnvFile: An optional environment file to load.
|
||||
/// - logger: An optional logger to use for debugging.
|
||||
/// - version: A version that is specified from command line, ignoring any environment variable.
|
||||
static func load(
|
||||
dotEnvFile: String?,
|
||||
logger: Logger?,
|
||||
version: String?
|
||||
) throws -> EnvVars {
|
||||
) async throws -> EnvVars {
|
||||
@Dependency(\.environment) var environment
|
||||
|
||||
let defaultEnvVars = EnvVars()
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
if let dotEnvFile {
|
||||
try DotEnv.load(path: dotEnvFile)
|
||||
}
|
||||
|
||||
let defaultEnvDict = (try? encoder.encode(defaultEnvVars))
|
||||
.flatMap { try? decoder.decode([String: String].self, from: $0) }
|
||||
?? [:]
|
||||
|
||||
let dotEnvDict = try await environment.dotEnvDict(path: dotEnvFile)
|
||||
let envVarsDict = defaultEnvDict
|
||||
.merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 })
|
||||
.merging(environment.processInfo(), uniquingKeysWith: { $1 })
|
||||
.merging(dotEnvDict, uniquingKeysWith: { $1 })
|
||||
|
||||
var envVars = (try? JSONSerialization.data(withJSONObject: envVarsDict))
|
||||
.flatMap { try? decoder.decode(EnvVars.self, from: $0) }
|
||||
@@ -125,7 +150,7 @@ extension EnvVars {
|
||||
@_spi(Internal)
|
||||
public extension MQTTClient {
|
||||
convenience init(
|
||||
envVars: EnvVars,
|
||||
environment envVars: EnvVars,
|
||||
eventLoopGroup: EventLoopGroup,
|
||||
logger: Logger?
|
||||
) {
|
||||
|
||||
71
Sources/CliClient/EnvironmentDependency.swift
Normal file
71
Sources/CliClient/EnvironmentDependency.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
import Dependencies
|
||||
import DependenciesMacros
|
||||
import DotEnv
|
||||
import Foundation
|
||||
import Models
|
||||
|
||||
@_spi(Internal)
|
||||
public extension DependencyValues {
|
||||
var environment: EnvironmentDependency {
|
||||
get { self[EnvironmentDependency.self] }
|
||||
set { self[EnvironmentDependency.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
/// Responsible for loading environment variables and files.
|
||||
///
|
||||
///
|
||||
@_spi(Internal)
|
||||
@DependencyClient
|
||||
public struct EnvironmentDependency: Sendable {
|
||||
|
||||
/// Load the variables based on the request.
|
||||
public var load: @Sendable (FileType) async throws -> [String: String] = { _ in [:] }
|
||||
|
||||
public var processInfo: @Sendable () -> [String: String] = { [:] }
|
||||
|
||||
public enum FileType: Equatable {
|
||||
case dotEnv(path: String)
|
||||
case json(path: String)
|
||||
|
||||
init?(path: String) {
|
||||
let strings = path.split(separator: ".")
|
||||
guard let ext = strings.last else {
|
||||
return nil
|
||||
}
|
||||
switch ext {
|
||||
case "env":
|
||||
self = .dotEnv(path: path)
|
||||
case "json":
|
||||
self = .json(path: path)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@_spi(Internal)
|
||||
extension EnvironmentDependency: DependencyKey {
|
||||
|
||||
public static let testValue: EnvironmentDependency = Self()
|
||||
|
||||
public static let liveValue: EnvironmentDependency = Self(
|
||||
load: { file in
|
||||
switch file {
|
||||
case let .dotEnv(path: path):
|
||||
let file = try DotEnv.read(path: path)
|
||||
return file.lines.reduce(into: [String: String]()) { partialResult, line in
|
||||
partialResult[line.key] = line.value
|
||||
}
|
||||
case let .json(path: path):
|
||||
let url = URL(filePath: path)
|
||||
return try JSONDecoder().decode(
|
||||
[String: String].self,
|
||||
from: Data(contentsOf: url)
|
||||
)
|
||||
}
|
||||
},
|
||||
processInfo: { ProcessInfo.processInfo.environment }
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user