Files
swift-hpa/Sources/CommandClient/CommandClient.swift

114 lines
2.7 KiB
Swift

import Dependencies
import DependenciesMacros
import Foundation
import ShellClient
public extension DependencyValues {
/// Runs shell commands.
var commandClient: CommandClient {
get { self[CommandClient.self] }
set { self[CommandClient.self] = newValue }
}
}
@DependencyClient
public struct CommandClient: Sendable {
/// Runs a shell command.
public var runCommand: @Sendable (RunCommandOptions) async throws -> Void
/// Runs a shell command.
public func run(
quiet: Bool,
shell: ShellCommand.Shell,
_ arguments: [String]
) async throws {
try await runCommand(.init(arguments: arguments, quiet: quiet, shell: shell))
}
/// Runs a shell command.
public func run(
quiet: Bool,
shell: ShellCommand.Shell,
_ arguments: String...
) async throws {
try await run(quiet: quiet, shell: shell, arguments)
}
public struct RunCommandOptions: Sendable, Equatable {
public let arguments: [String]
public let quiet: Bool
public let shell: ShellCommand.Shell
public init(
arguments: [String],
quiet: Bool,
shell: ShellCommand.Shell
) {
self.arguments = arguments
self.quiet = quiet
self.shell = shell
}
}
}
extension CommandClient: DependencyKey {
public static let testValue: CommandClient = Self()
public static var liveValue: CommandClient {
.init { options in
@Dependency(\.asyncShellClient) var shellClient
if !options.quiet {
try await shellClient.foreground(.init(
shell: options.shell,
environment: ProcessInfo.processInfo.environment,
in: nil,
options.arguments
))
} else {
try await shellClient.background(.init(
shell: options.shell,
environment: ProcessInfo.processInfo.environment,
in: nil,
options.arguments
))
}
}
}
}
@_spi(Internal)
public extension CommandClient {
/// Create a command client that can capture the arguments / options.
///
/// This is used for testing.
static func capturing(_ client: CapturingClient) -> Self {
.init { options in
await client.set(options)
}
}
/// Captures the arguments / options passed into the command client's run commands.
///
actor CapturingClient: Sendable {
public private(set) var quiet: Bool?
public private(set) var shell: ShellCommand.Shell?
public private(set) var arguments: [String]?
public init() {}
public func set(
_ options: RunCommandOptions
) {
quiet = options.quiet
shell = options.shell
arguments = options.arguments
}
}
}
extension ShellCommand.Shell: @retroactive @unchecked Sendable {}