Skip to content

Commit

Permalink
Support passing arguments to async main (apple#568)
Browse files Browse the repository at this point in the history
* Support passing arguments to async main

* Add test for AsyncParsableCommand.main()
  • Loading branch information
Austinpayne authored May 17, 2024
1 parent 1d191b0 commit 29b3d39
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 6 deletions.
30 changes: 24 additions & 6 deletions Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ public protocol AsyncParsableCommand: ParsableCommand {

@available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *)
extension AsyncParsableCommand {
/// Executes this command, or one of its subcommands, with the program's
/// command-line arguments.
/// Executes this command, or one of its subcommands, with the given arguments.
///
/// Instead of calling this method directly, you can add `@main` to the root
/// command for your command-line tool.
public static func main() async {
/// This method parses an instance of this type, one of its subcommands, or
/// another built-in `AsyncParsableCommand` type, from command-line
/// (or provided) arguments, and then calls its `run()` method, exiting
/// with a relevant error message if necessary.
///
/// - Parameter arguments: An array of arguments to use for parsing. If
/// `arguments` is `nil`, this uses the program's command-line arguments.
public static func main(_ arguments: [String]?) async {
do {
var command = try parseAsRoot()
var command = try parseAsRoot(arguments)
if var asyncCommand = command as? AsyncParsableCommand {
try await asyncCommand.run()
} else {
Expand All @@ -42,6 +46,20 @@ extension AsyncParsableCommand {
exit(withError: error)
}
}

/// Executes this command, or one of its subcommands, with the program's
/// command-line arguments.
///
/// Instead of calling this method directly, you can add `@main` to the root
/// command for your command-line tool.
///
/// This method parses an instance of this type, one of its subcommands, or
/// another built-in `AsyncParsableCommand` type, from command-line arguments,
/// and then calls its `run()` method, exiting with a relevant error message
/// if necessary.
public static func main() async {
await self.main(nil)
}
}

/// A type that can designate an `AsyncParsableCommand` as the program's
Expand Down
69 changes: 69 additions & 0 deletions Tests/ArgumentParserEndToEndTests/AsyncCommandEndToEndTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import XCTest
import ArgumentParser

final class AsyncCommandEndToEndTests: XCTestCase {
}

actor AsyncStatusCheck {
struct Status: OptionSet {
var rawValue: UInt8

static var root: Self { .init(rawValue: 1 << 0) }
static var sub: Self { .init(rawValue: 1 << 1) }
}

@MainActor
var status: Status = []

@MainActor
func update(_ status: Status) {
self.status.insert(status)
}
}

var statusCheck = AsyncStatusCheck()

// MARK: AsyncParsableCommand.main() testing

struct AsyncCommand: AsyncParsableCommand {
static var configuration: CommandConfiguration {
.init(subcommands: [SubCommand.self])
}

func run() async throws {
await statusCheck.update(.root)
}

struct SubCommand: AsyncParsableCommand {
func run() async throws {
await statusCheck.update(.sub)
}
}
}

extension AsyncCommandEndToEndTests {
@MainActor
func testAsyncMain_root() async throws {
XCTAssertFalse(statusCheck.status.contains(.root))
await AsyncCommand.main([])
XCTAssertTrue(statusCheck.status.contains(.root))
}

@MainActor
func testAsyncMain_sub() async throws {
XCTAssertFalse(statusCheck.status.contains(.sub))
await AsyncCommand.main(["sub-command"])
XCTAssertTrue(statusCheck.status.contains(.sub))
}
}
1 change: 1 addition & 0 deletions Tests/ArgumentParserEndToEndTests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_library(EndToEndTests
AsyncCommandEndToEndTests.swift
CustomParsingEndToEndTests.swift
DefaultsEndToEndTests.swift
EnumEndToEndTests.swift
Expand Down

0 comments on commit 29b3d39

Please sign in to comment.