Skip to content

Commit

Permalink
Revise async commands support (#196)
Browse files Browse the repository at this point in the history
* Bump NIO version requirement
* Deprecate unneeded and functionally incorrect APIs
* Simplify and correct sync command adapters.
* Remove excess imports, avoid concurrency warnings about fflush(), and a bit of code style
  • Loading branch information
gwynne committed Dec 6, 2023
1 parent 18262d2 commit a7dd700
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Package@swift-5.9.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
32 changes: 0 additions & 32 deletions Sources/ConsoleKitCommands/Async/AsyncCommandGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 3 additions & 6 deletions Sources/ConsoleKitCommands/Base/AnyCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) })
}
}
}
8 changes: 2 additions & 6 deletions Sources/ConsoleKitCommands/Base/CommandGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
7 changes: 1 addition & 6 deletions Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
10 changes: 2 additions & 8 deletions Sources/ConsoleKitTerminal/Output/Console+Wait.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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)
}
}
18 changes: 11 additions & 7 deletions Sources/ConsoleKitTerminal/Terminal/ANSI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,21 @@ 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+"))

}
}

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()
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)"
}
}
7 changes: 0 additions & 7 deletions Sources/ConsoleKitTerminal/Terminal/Terminal.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
#if canImport(Darwin)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif os(Windows)
import CRT
#endif
import Foundation
import NIOConcurrencyHelpers

Expand Down
13 changes: 1 addition & 12 deletions Sources/ConsoleKitTerminal/Utilities/LoggerFragment.swift
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down

0 comments on commit a7dd700

Please sign in to comment.