feat: Begins using swift argument parser and creating cli client dependency
All checks were successful
CI / Run Tests (push) Successful in 4m27s
All checks were successful
CI / Run Tests (push) Successful in 4m27s
This commit is contained in:
188
Sources/CliClient/CliClient.swift
Normal file
188
Sources/CliClient/CliClient.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user