feat: Moves logging extensions into it's own module, moves configuration merging into config-client, fixes merging bug.

This commit is contained in:
2024-12-25 20:06:52 -05:00
parent fbb4a22e98
commit 56359f3488
11 changed files with 269 additions and 135 deletions

View File

@@ -47,18 +47,32 @@ public extension CliClient.SharedOptions {
func withMergedConfiguration<T>(
operation: (Configuration) async throws -> T
) async throws -> T {
try await withConfiguration(path: configurationFile) { configuration in
var configuration = configuration
configuration = configuration.mergingTarget(target)
@Dependency(\.configurationClient) var configurationClient
@Dependency(\.logger) var logger
if configuration.strategy?.branch != nil, let branch {
configuration = configuration.mergingStrategy(.branch(branch))
} else if let semvar {
configuration = configuration.mergingStrategy(.semvar(semvar))
}
var strategy: Configuration.VersionStrategy?
return try await operation(configuration)
if let branch {
logger.trace("Merging branch strategy.")
strategy = .branch(branch)
} else if let semvar {
logger.trace("Merging semvar strategy.")
var semvarString = ""
customDump(semvar, to: &semvarString)
logger.trace("\(semvarString)")
strategy = .semvar(semvar)
}
let configuration = Configuration(
target: target,
strategy: strategy
)
return try await configurationClient.withConfiguration(
path: configurationFile,
merging: configuration,
operation: operation
)
}
func write(_ string: String, to url: URL) async throws {

View File

@@ -1,82 +1,82 @@
import ConfigurationClient
import Dependencies
import FileClient
import Foundation
extension Configuration {
func mergingTarget(_ otherTarget: Configuration.Target?) -> Self {
.init(
target: otherTarget ?? target,
strategy: strategy
)
}
func mergingStrategy(_ otherStrategy: Configuration.VersionStrategy?) -> Self {
.init(
target: target,
strategy: strategy?.merging(otherStrategy)
)
}
}
extension Configuration.PreRelease {
func merging(_ other: Self?) -> Self {
.init(
prefix: other?.prefix ?? prefix,
strategy: other?.strategy ?? strategy
)
}
}
extension Configuration.Branch {
func merging(_ other: Self?) -> Self {
return .init(includeCommitSha: other?.includeCommitSha ?? includeCommitSha)
}
}
extension Configuration.SemVar {
// TODO: Merge strategy ??
func merging(_ other: Self?) -> Self {
.init(
allowPreRelease: other?.allowPreRelease ?? allowPreRelease,
preRelease: preRelease?.merging(other?.preRelease),
requireExistingFile: other?.requireExistingFile ?? requireExistingFile,
requireExistingSemVar: other?.requireExistingSemVar ?? requireExistingSemVar,
strategy: other?.strategy ?? strategy
)
}
}
extension Configuration.VersionStrategy {
func merging(_ other: Self?) -> Self {
guard let branch else {
guard let semvar else { return self }
return .semvar(semvar.merging(other?.semvar))
}
return .branch(branch.merging(other?.branch))
}
}
extension Configuration {
func merging(_ other: Self?) -> Self {
var output = self
output = output.mergingTarget(other?.target)
output = output.mergingStrategy(other?.strategy)
return output
}
}
@discardableResult
func withConfiguration<T>(
path: String?,
_ operation: (Configuration) async throws -> T
) async throws -> T {
@Dependency(\.configurationClient) var configurationClient
let configuration = try await configurationClient.findAndLoad(
path != nil ? URL(filePath: path!) : nil
)
return try await operation(configuration)
}
// import ConfigurationClient
// import Dependencies
// import FileClient
// import Foundation
//
// extension Configuration {
//
// func mergingTarget(_ otherTarget: Configuration.Target?) -> Self {
// .init(
// target: otherTarget ?? target,
// strategy: strategy
// )
// }
//
// func mergingStrategy(_ otherStrategy: Configuration.VersionStrategy?) -> Self {
// .init(
// target: target,
// strategy: strategy?.merging(otherStrategy)
// )
// }
// }
//
// extension Configuration.PreRelease {
// func merging(_ other: Self?) -> Self {
// .init(
// prefix: other?.prefix ?? prefix,
// strategy: other?.strategy ?? strategy
// )
// }
// }
//
// extension Configuration.Branch {
// func merging(_ other: Self?) -> Self {
// return .init(includeCommitSha: other?.includeCommitSha ?? includeCommitSha)
// }
// }
//
// extension Configuration.SemVar {
// // TODO: Merge strategy ??
// func merging(_ other: Self?) -> Self {
// .init(
// allowPreRelease: other?.allowPreRelease ?? allowPreRelease,
// preRelease: preRelease?.merging(other?.preRelease),
// requireExistingFile: other?.requireExistingFile ?? requireExistingFile,
// requireExistingSemVar: other?.requireExistingSemVar ?? requireExistingSemVar,
// strategy: other?.strategy ?? strategy
// )
// }
// }
//
// extension Configuration.VersionStrategy {
// func merging(_ other: Self?) -> Self {
// guard let branch else {
// guard let semvar else { return self }
// return .semvar(semvar.merging(other?.semvar))
// }
// return .branch(branch.merging(other?.branch))
// }
// }
//
// extension Configuration {
// func merging(_ other: Self?) -> Self {
// var output = self
// output = output.mergingTarget(other?.target)
// output = output.mergingStrategy(other?.strategy)
// return output
// }
// }
//
// @discardableResult
// func withConfiguration<T>(
// path: String?,
// _ operation: (Configuration) async throws -> T
// ) async throws -> T {
// @Dependency(\.configurationClient) var configurationClient
//
// let configuration = try await configurationClient.findAndLoad(
// path != nil ? URL(filePath: path!) : nil
// )
//
// return try await operation(configuration)
// }

View File

@@ -1,172 +0,0 @@
import Dependencies
import Foundation
import Logging
import LoggingFormatAndPipe
import Rainbow
import ShellClient
extension String {
var orange: Self {
bit24(255, 165, 0)
}
var magena: Self {
// bit24(186, 85, 211)
bit24(238, 130, 238)
}
}
@_spi(Internal)
public extension Logger.Level {
init(verbose: Int) {
switch verbose {
case 1: self = .debug
case 2...: self = .trace
default: self = .warning
}
}
var coloredString: String {
switch self {
case .info:
return "\(self)".cyan
case .warning:
return "\(self)".orange.bold
case .debug:
return "\(self)".green
case .trace:
return "\(self)".yellow
case .error:
return "\(self)".red.bold
default:
return "\(self)"
}
}
}
struct LevelFormatter: LoggingFormatAndPipe.Formatter {
let basic: BasicFormatter
var timestampFormatter: DateFormatter { basic.timestampFormatter }
// swiftlint:disable function_parameter_count
func processLog(
level: Logger.Level,
message: Logger.Message,
prettyMetadata: String?,
file: String,
function: String,
line: UInt
) -> String {
let now = Date()
return basic.format.map { component -> String in
return processComponent(
component,
now: now,
level: level,
message: message,
prettyMetadata: prettyMetadata,
file: file,
function: function,
line: line
)
}
.filter { $0.count > 0 }
.joined(separator: basic.separator ?? "")
}
public func processComponent(
_ component: LogComponent,
now: Date,
level: Logger.Level,
message: Logger.Message,
prettyMetadata: String?,
file: String,
function: String,
line: UInt
) -> String {
switch component {
case .level:
let maxLen = "\(Logger.Level.warning)".count
let paddingCount = (maxLen - "\(level)".count) / 2
var padding = ""
for _ in 0 ... paddingCount {
padding += " "
}
return "\(padding)\(level.coloredString)\(padding)"
case let .group(components):
return components.map { component -> String in
self.processComponent(
component,
now: now,
level: level,
message: message,
prettyMetadata: prettyMetadata,
file: file,
function: function,
line: line
)
}.joined()
case .message:
return basic.processComponent(
component,
now: now,
level: level,
message: message,
prettyMetadata: prettyMetadata,
file: file,
function: function,
line: line
).italic
default:
return basic.processComponent(
component,
now: now,
level: level,
message: message,
prettyMetadata: prettyMetadata,
file: file,
function: function,
line: line
)
}
}
// swiftlint:enable function_parameter_count
}
extension CliClient.LoggingOptions {
func makeLogger() -> Logger {
let formatters: [LogComponent] = [
.text(executableName.magena),
.text(command.blue),
.level,
.group([
.text("-> "),
.message
])
]
return Logger(label: executableName) { _ in
LoggingFormatAndPipe.Handler(
formatter: LevelFormatter(basic: BasicFormatter(
formatters,
separator: " | "
)),
pipe: LoggerTextOutputStreamPipe.standardOutput
)
}
}
func withLogger<T>(_ operation: () async throws -> T) async rethrows -> T {
try await withDependencies {
$0.logger = makeLogger()
$0.logger.logLevel = .init(verbose: verbose)
} operation: {
try await operation()
}
}
}