feat: Begins using swift argument parser and creating cli client dependency
All checks were successful
CI / Run Tests (push) Successful in 4m27s

This commit is contained in:
2024-11-16 22:32:32 -05:00
parent 3416ce1003
commit 6472d3cd1e
19 changed files with 657 additions and 342 deletions

View File

@@ -0,0 +1,188 @@
import Dependencies
import DependenciesMacros
import DotEnv
import Foundation
import Logging
import Models
import MQTTNIO
import NIO
public extension DependencyValues {
var cliClient: CliClient {
get { self[CliClient.self] }
set { self[CliClient.self] = newValue }
}
}
@DependencyClient
public struct CliClient {
public var logLevel: @Sendable (EnvVars) -> Logger.Level = { _ in .debug }
public var makeEnvVars: @Sendable (EnvVarsRequest) async throws -> EnvVars
public var makeClient: @Sendable (ClientRequest) throws -> MQTTClient
public var parseMqttClientVersion: @Sendable (String) -> MQTTClient.Version?
public struct ClientRequest: Sendable {
public let envVars: EnvVars
public let eventLoopGroup: MultiThreadedEventLoopGroup
public let logger: Logger?
public init(
envVars: EnvVars,
eventLoopGroup: MultiThreadedEventLoopGroup,
logger: Logger?
) {
self.envVars = envVars
self.eventLoopGroup = eventLoopGroup
self.logger = logger
}
}
public struct EnvVarsRequest: Sendable {
public let envFilePath: String?
public let logger: Logger?
public let mqttClientVersion: String?
public init(
envFilePath: String? = nil,
logger: Logger? = nil,
version mqttClientVersion: String? = nil
) {
self.envFilePath = envFilePath
self.logger = logger
self.mqttClientVersion = mqttClientVersion
}
}
}
extension CliClient: DependencyKey {
public static let testValue: CliClient = Self()
public static var liveValue: CliClient {
Self(
logLevel: { Logger.Level.from(environment: $0) },
makeEnvVars: {
try EnvVars.load(
dotEnvFile: $0.envFilePath,
logger: $0.logger,
version: $0.mqttClientVersion
)
},
makeClient: {
MQTTClient(
envVars: $0.envVars,
eventLoopGroup: $0.eventLoopGroup,
logger: $0.logger
)
},
parseMqttClientVersion: { .init(string: $0) }
)
}
}
extension EnvVars {
/// Load the `EnvVars` from the environment.
///
/// - Paramaters:
/// - 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 {
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 envVarsDict = defaultEnvDict
.merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 })
var envVars = (try? JSONSerialization.data(withJSONObject: envVarsDict))
.flatMap { try? decoder.decode(EnvVars.self, from: $0) }
?? defaultEnvVars
if let version {
envVars.version = version
}
logger?.debug("Done loading EnvVars...")
return envVars
}
}
@_spi(Internal)
public extension MQTTClient {
convenience init(
envVars: EnvVars,
eventLoopGroup: EventLoopGroup,
logger: Logger?
) {
self.init(
host: envVars.host,
port: envVars.port != nil ? Int(envVars.port!) : nil,
identifier: envVars.identifier,
eventLoopGroupProvider: .shared(eventLoopGroup),
logger: logger,
configuration: .init(
version: .parseOrDefualt(string: envVars.version),
disablePing: false,
userName: envVars.userName,
password: envVars.password
)
)
}
}
extension MQTTClient.Version {
static let `default` = Self.v3_1_1
static func parseOrDefualt(string: String?) -> Self {
guard let string, let value = Self(string: string) else {
return .default
}
return value
}
init?(string: String) {
if string.contains("5") {
self = .v5_0
} else if string.contains("3") {
self = .v3_1_1
} else {
return nil
}
}
}
@_spi(Internal)
public extension Logger.Level {
/// Parse a `Logger.Level` from the loaded `EnvVars`.
static func from(environment envVars: EnvVars) -> Self {
// If the log level was set via an environment variable.
if let logLevel = envVars.logLevel {
return logLevel
}
// Parse the appEnv to derive an log level.
switch envVars.appEnv {
case .staging, .development:
return .debug
case .production:
return .info
case .testing:
return .trace
}
}
}