Peekaboo/peekaboo-cli/Sources/peekaboo/AsyncAdapter.swift
Peter Steinberger 559349f198 Fix Swift 6 async execution with synchronous adapter
- Add AsyncAdapter.swift to bridge async/sync execution
- Change AsyncParsableCommand back to ParsableCommand
- Implement AsyncRunnable protocol for async execution
- Use DispatchSemaphore pattern for synchronous blocking
- Make ErrorBox thread-safe with @unchecked Sendable

This fixes the CLI execution issue where commands were showing help
instead of executing, by properly bridging the async/sync worlds
as required by ArgumentParser.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:34:53 +01:00

140 lines
No EOL
3.1 KiB
Swift

import Foundation
import ArgumentParser
// MARK: - Adapter for AsyncParsableCommand to ParsableCommand bridge
protocol AsyncRunnable {
func runAsync() async throws
}
extension PeekabooCommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
extension ImageCommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
extension ListCommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
extension AppsSubcommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
extension WindowsSubcommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
extension ServerStatusSubcommand {
func run() throws {
let box = ErrorBox()
let sem = DispatchSemaphore(value: 0)
Task {
defer { sem.signal() }
do {
try await (self as AsyncRunnable).runAsync()
} catch {
box.error = error
}
}
sem.wait()
if let error = box.error {
throw error
}
}
}
private final class ErrorBox: @unchecked Sendable {
private var _error: Error? = nil
private let lock = NSLock()
var error: Error? {
get {
lock.lock()
defer { lock.unlock() }
return _error
}
set {
lock.lock()
defer { lock.unlock() }
_error = newValue
}
}
}