Skip to content

Commit 29b3d39

Browse files
authored
Support passing arguments to async main (#568)
* Support passing arguments to async main * Add test for AsyncParsableCommand.main()
1 parent 1d191b0 commit 29b3d39

File tree

3 files changed

+94
-6
lines changed

3 files changed

+94
-6
lines changed

Sources/ArgumentParser/Parsable Types/AsyncParsableCommand.swift

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ public protocol AsyncParsableCommand: ParsableCommand {
2525

2626
@available(macOS 10.15, macCatalyst 13, iOS 13, tvOS 13, watchOS 6, *)
2727
extension AsyncParsableCommand {
28-
/// Executes this command, or one of its subcommands, with the program's
29-
/// command-line arguments.
28+
/// Executes this command, or one of its subcommands, with the given arguments.
3029
///
31-
/// Instead of calling this method directly, you can add `@main` to the root
32-
/// command for your command-line tool.
33-
public static func main() async {
30+
/// This method parses an instance of this type, one of its subcommands, or
31+
/// another built-in `AsyncParsableCommand` type, from command-line
32+
/// (or provided) arguments, and then calls its `run()` method, exiting
33+
/// with a relevant error message if necessary.
34+
///
35+
/// - Parameter arguments: An array of arguments to use for parsing. If
36+
/// `arguments` is `nil`, this uses the program's command-line arguments.
37+
public static func main(_ arguments: [String]?) async {
3438
do {
35-
var command = try parseAsRoot()
39+
var command = try parseAsRoot(arguments)
3640
if var asyncCommand = command as? AsyncParsableCommand {
3741
try await asyncCommand.run()
3842
} else {
@@ -42,6 +46,20 @@ extension AsyncParsableCommand {
4246
exit(withError: error)
4347
}
4448
}
49+
50+
/// Executes this command, or one of its subcommands, with the program's
51+
/// command-line arguments.
52+
///
53+
/// Instead of calling this method directly, you can add `@main` to the root
54+
/// command for your command-line tool.
55+
///
56+
/// This method parses an instance of this type, one of its subcommands, or
57+
/// another built-in `AsyncParsableCommand` type, from command-line arguments,
58+
/// and then calls its `run()` method, exiting with a relevant error message
59+
/// if necessary.
60+
public static func main() async {
61+
await self.main(nil)
62+
}
4563
}
4664

4765
/// A type that can designate an `AsyncParsableCommand` as the program's
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===----------------------------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift Argument Parser open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import XCTest
13+
import ArgumentParser
14+
15+
final class AsyncCommandEndToEndTests: XCTestCase {
16+
}
17+
18+
actor AsyncStatusCheck {
19+
struct Status: OptionSet {
20+
var rawValue: UInt8
21+
22+
static var root: Self { .init(rawValue: 1 << 0) }
23+
static var sub: Self { .init(rawValue: 1 << 1) }
24+
}
25+
26+
@MainActor
27+
var status: Status = []
28+
29+
@MainActor
30+
func update(_ status: Status) {
31+
self.status.insert(status)
32+
}
33+
}
34+
35+
var statusCheck = AsyncStatusCheck()
36+
37+
// MARK: AsyncParsableCommand.main() testing
38+
39+
struct AsyncCommand: AsyncParsableCommand {
40+
static var configuration: CommandConfiguration {
41+
.init(subcommands: [SubCommand.self])
42+
}
43+
44+
func run() async throws {
45+
await statusCheck.update(.root)
46+
}
47+
48+
struct SubCommand: AsyncParsableCommand {
49+
func run() async throws {
50+
await statusCheck.update(.sub)
51+
}
52+
}
53+
}
54+
55+
extension AsyncCommandEndToEndTests {
56+
@MainActor
57+
func testAsyncMain_root() async throws {
58+
XCTAssertFalse(statusCheck.status.contains(.root))
59+
await AsyncCommand.main([])
60+
XCTAssertTrue(statusCheck.status.contains(.root))
61+
}
62+
63+
@MainActor
64+
func testAsyncMain_sub() async throws {
65+
XCTAssertFalse(statusCheck.status.contains(.sub))
66+
await AsyncCommand.main(["sub-command"])
67+
XCTAssertTrue(statusCheck.status.contains(.sub))
68+
}
69+
}

Tests/ArgumentParserEndToEndTests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_library(EndToEndTests
2+
AsyncCommandEndToEndTests.swift
23
CustomParsingEndToEndTests.swift
34
DefaultsEndToEndTests.swift
45
EnumEndToEndTests.swift

0 commit comments

Comments
 (0)