diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 43937c1..9c7c7ba 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -24,7 +24,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.56.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"), ], targets: [ .target( diff --git a/Sources/ConsoleKitCommands/Async/AsyncCommandGroup.swift b/Sources/ConsoleKitCommands/Async/AsyncCommandGroup.swift index a90a87a..345a8f6 100644 --- a/Sources/ConsoleKitCommands/Async/AsyncCommandGroup.swift +++ b/Sources/ConsoleKitCommands/Async/AsyncCommandGroup.swift @@ -18,42 +18,10 @@ import ConsoleKitTerminal public protocol AsyncCommandGroup: AnyAsyncCommand { var commands: [String: any AnyAsyncCommand] { get } var defaultCommand: (any AnyAsyncCommand)? { get } - - /// Merges this group with another group. - /// - Parameters: - /// - group: The group to merge with. - /// - defaultCommand: The new default command to use. - /// - help: The help message to use for the merged group. - /// - Returns: A new `AsyncCommandGroup` with the merged commands. - func merge( - with group: any AsyncCommandGroup, - defaultCommand: (any AnyAsyncCommand)?, - help: String - ) -> any AsyncCommandGroup -} - -public struct MergedAsyncCommandGroup: AsyncCommandGroup { - public let commands: [String: any AnyAsyncCommand] - public let defaultCommand: (any AnyAsyncCommand)? - public var help: String } extension AsyncCommandGroup { public var defaultCommand: (any AnyAsyncCommand)? { nil } - - public func merge( - with group: any AsyncCommandGroup, - defaultCommand: (any AnyAsyncCommand)?, - help: String - ) -> any AsyncCommandGroup { - var mergedCommands = self.commands - mergedCommands.merge(group.commands, uniquingKeysWith: { (_, new) in new }) - return MergedAsyncCommandGroup( - commands: mergedCommands, - defaultCommand: defaultCommand, - help: help - ) - } } extension AsyncCommandGroup { diff --git a/Sources/ConsoleKitCommands/Base/AnyCommand.swift b/Sources/ConsoleKitCommands/Base/AnyCommand.swift index 84ab2bb..9a26eb3 100644 --- a/Sources/ConsoleKitCommands/Base/AnyCommand.swift +++ b/Sources/ConsoleKitCommands/Base/AnyCommand.swift @@ -25,12 +25,9 @@ extension AnyCommand { "" } - // we need to have a sync environment so the compiler uses the sync run method over the async version - private func syncRun(using context: inout CommandContext) throws { - try self.run(using: &context) - } - public func run(using context: inout CommandContext) async throws { - try self.syncRun(using: &context) + try await withCheckedThrowingContinuation { continuation in + continuation.resume(with: .init { try self.run(using: &context) }) + } } } diff --git a/Sources/ConsoleKitCommands/Base/CommandGroup.swift b/Sources/ConsoleKitCommands/Base/CommandGroup.swift index b838590..b1110c5 100644 --- a/Sources/ConsoleKitCommands/Base/CommandGroup.swift +++ b/Sources/ConsoleKitCommands/Base/CommandGroup.swift @@ -21,14 +21,10 @@ public protocol CommandGroup: AnyCommand, AsyncCommandGroup { } extension CommandGroup { - public var defaultCommand: (any AnyCommand)? { - nil - } + public var defaultCommand: (any AnyCommand)? { nil } public var commands: [String: any AnyAsyncCommand] { - // make the compiler happy - let castedCommands: [String: any AnyCommand] = commands - return castedCommands + self.commands as [String: any AnyCommand] } } diff --git a/Sources/ConsoleKitCommands/Deprecated/MergedAsyncCommandGroup.swift b/Sources/ConsoleKitCommands/Deprecated/MergedAsyncCommandGroup.swift new file mode 100644 index 0000000..e0e3c78 --- /dev/null +++ b/Sources/ConsoleKitCommands/Deprecated/MergedAsyncCommandGroup.swift @@ -0,0 +1,17 @@ +@available(*, deprecated, message: "This API should not have been made public and is obsolete; do not use it.") +public struct MergedAsyncCommandGroup: AsyncCommandGroup { + public let commands: [String: any AnyAsyncCommand] + public let defaultCommand: (any AnyAsyncCommand)? + public var help: String +} + +extension AsyncCommandGroup { + @available(*, deprecated, message: "This API should not have been made public and is obsolete; do not use it.") + public func merge( + with group: any AsyncCommandGroup, + defaultCommand: (any AnyAsyncCommand)?, + help: String + ) -> any AsyncCommandGroup { + MergedAsyncCommandGroup(commands: self.commands.merging(group.commands, uniquingKeysWith: { $1 }), defaultCommand: defaultCommand, help: help) + } +} diff --git a/Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift b/Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift index 66b33c5..41bd025 100644 --- a/Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift +++ b/Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift @@ -1,14 +1,9 @@ -#if os(Linux) +#if !canImport(Darwin) // Needed because DispatchQueue isn't Sendable on Linux @preconcurrency import Foundation #else import Foundation #endif -#if canImport(Darwin) -import Darwin -#else -import Glibc -#endif import NIOConcurrencyHelpers extension ActivityIndicatorType { diff --git a/Sources/ConsoleKitTerminal/Output/Console+Wait.swift b/Sources/ConsoleKitTerminal/Output/Console+Wait.swift index 110a81b..4f5c359 100644 --- a/Sources/ConsoleKitTerminal/Output/Console+Wait.swift +++ b/Sources/ConsoleKitTerminal/Output/Console+Wait.swift @@ -1,8 +1,4 @@ -#if canImport(Darwin) -import Darwin -#else -import Glibc -#endif +import Foundation extension Console { /// Blocks the current thread for the specified number of seconds. @@ -14,8 +10,6 @@ extension Console { /// - parameters: /// - seconds: The number of seconds to wait for. public func wait(seconds: Double) { - let factor = 1000 * 1000 - let microseconds = seconds * Double(factor) - usleep(useconds_t(microseconds)) + Thread.sleep(forTimeInterval: seconds) } } diff --git a/Sources/ConsoleKitTerminal/Terminal/ANSI.swift b/Sources/ConsoleKitTerminal/Terminal/ANSI.swift index ba2712f..45c26aa 100644 --- a/Sources/ConsoleKitTerminal/Terminal/ANSI.swift +++ b/Sources/ConsoleKitTerminal/Terminal/ANSI.swift @@ -43,9 +43,13 @@ enum ANSISGRCommand { extension Terminal { /// Performs an `ANSICommand`. func command(_ command: ANSICommand) { - guard enableCommands else { return } + guard self.enableCommands else { return } Swift.print(command.ansi, terminator: "") - fflush(stdout) + + // fdopen() on stdout is fast; also the returned file MUST NOT be fclose()d + // This avoids concurrency complaints due to accessing global `stdout`. + fflush(fdopen(STDOUT_FILENO, "w+")) + } } @@ -53,7 +57,7 @@ extension ConsoleText { /// Wraps a string in the ANSI codes indicated /// by the style specification func terminalStylize() -> String { - return fragments + self.fragments .map { $0.string.terminalStylize($0.style) } .joined() } @@ -180,13 +184,13 @@ extension ConsoleStyle { var ansiCommand: ANSICommand { var commands: [ANSISGRCommand] = [.reset] - if isBold { + if self.isBold { commands.append(.bold) } - if let color = color { + if let color = self.color { commands.append(color.ansiSpec.foregroundAnsiCommand) } - if let background = background { + if let background = self.background { commands.append(background.ansiSpec.backgroundAnsiCommand) } return .sgr(commands) @@ -196,6 +200,6 @@ extension ConsoleStyle { extension String { /// Converts a String to a full ANSI command. fileprivate var ansi: String { - return "\u{001B}[" + self + "\u{001B}[\(self)" } } diff --git a/Sources/ConsoleKitTerminal/Terminal/Terminal.swift b/Sources/ConsoleKitTerminal/Terminal/Terminal.swift index 9c6ef78..67af283 100644 --- a/Sources/ConsoleKitTerminal/Terminal/Terminal.swift +++ b/Sources/ConsoleKitTerminal/Terminal/Terminal.swift @@ -1,10 +1,3 @@ -#if canImport(Darwin) -import Darwin -#elseif canImport(Glibc) -import Glibc -#elseif os(Windows) -import CRT -#endif import Foundation import NIOConcurrencyHelpers diff --git a/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift b/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift index 9a6faf2..f0857a6 100644 --- a/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift +++ b/Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift @@ -1,16 +1,5 @@ import Logging - -#if canImport(Darwin) -import Darwin -#elseif os(Windows) -import CRT -#elseif canImport(Glibc) -import Glibc -#elseif canImport(WASILibc) -import WASILibc -#else -#error("Unsupported runtime") -#endif +import Foundation /// Information about a specific log message, including information from the logger the message was logged to. public struct LogRecord {