diff --git a/.gitignore b/.gitignore index 7fb530a..3cf30ac 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /Packages /*.xcodeproj Package.resolved +.swiftpm diff --git a/Package.swift b/Package.swift index c0c7a05..272daf5 100644 --- a/Package.swift +++ b/Package.swift @@ -1,14 +1,15 @@ -// swift-tools-version:5.10 +// swift-tools-version:6.0 -/* - This source file is part of the Swift.org open source project - - Copyright 2015 – 2021 Apple Inc. and the Swift project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See http://swift.org/LICENSE.txt for license information - See http://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ +//===----------------------------------------------------------------------===// +// +// This source file is part of the example package dealer open source project +// +// Copyright (c) 2015-2025 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 PackageDescription @@ -27,7 +28,7 @@ let package = Package( from: "3.0.0"), .package( url: "https://github.com/apple/swift-argument-parser.git", - from: "0.4.4"), + from: "1.6.0"), ], targets: [ .executableTarget( diff --git a/README.md b/README.md index 0b01df9..73116f2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,6 @@ To build this example package: git clone https://github.com/apple/example-package-dealer.git cd example-package-dealer - swift run + swift run dealer For more information, visit [Swift's package manager documentation](https://www.swift.org/package-manager/). diff --git a/Sources/dealer/Dealer.swift b/Sources/dealer/Dealer.swift index 57e832f..7c4d2fa 100644 --- a/Sources/dealer/Dealer.swift +++ b/Sources/dealer/Dealer.swift @@ -1,67 +1,74 @@ -/* - This source file is part of the Swift.org open source project +//===----------------------------------------------------------------------===// +// +// This source file is part of the example package dealer open source project +// +// Copyright (c) 2015-2025 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 +// +//===----------------------------------------------------------------------===// - Copyright 2015 – 2021 Apple Inc. and the Swift project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See http://swift.org/LICENSE.txt for license information - See http://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ +import ArgumentParser +import DeckOfPlayingCards +import Foundation +import PlayingCard #if os(Linux) -import Glibc + import Glibc #endif -import Foundation -import DeckOfPlayingCards -import PlayingCard -import ArgumentParser - @main struct Dealer: ParsableCommand { - enum Error: Swift.Error, CustomStringConvertible { - case notEnoughCards + enum Error: Swift.Error, CustomStringConvertible { + case notEnoughCards - var description: String { - switch self { - case .notEnoughCards: - return "Not enough cards" - } - } + var description: String { + switch self { + case .notEnoughCards: + return "Not enough cards" + } } + } - static var configuration = CommandConfiguration( - abstract: "Shuffles a deck of playing cards and deals a number of cards.", - discussion: """ - For each count argument, prints a line of tab-delimited cards to stdout, - or if there aren't enough cards remaining, - prints "Not enough cards" to stderr and exits with a nonzero status. - """) + static let configuration = CommandConfiguration( + abstract: "Shuffles a deck of playing cards and deals a number of cards.", + discussion: """ + For each count argument, prints a line of tab-delimited cards to stdout, + or if there aren't enough cards remaining, + prints "Not enough cards" to stderr and exits with a nonzero status. + """) - @Argument(help: .init("The number of cards to deal at a time.", - valueName: "count")) - var counts: [UInt] + @Argument( + help: .init( + "The number of cards to deal at a time.", + valueName: "count")) + var counts: [UInt] - mutating func run() throws { - #if os(Linux) - srandom(UInt32(clock())) - #endif + mutating func run() throws { + #if os(Linux) + srandom(UInt32(clock())) + #endif - var deck = Deck.standard52CardDeck() - deck.shuffle() + var deck = Deck.standard52CardDeck() + deck.shuffle() - for count in counts { - var cards: [PlayingCard] = [] + for count in counts { + var cards: [PlayingCard] = [] - for _ in 0.. (status: Int32, output: String?, error: String?) { - let process = Process() - process.executableURL = productsDirectory.appendingPathComponent("dealer") - process.arguments = arguments + @Test + func testDealOneHundredCards() throws { + let (status, output, error) = try execute(with: ["100"]) + #expect(status != EXIT_SUCCESS) + #expect(output?.isEmpty == true) - let outputPipe = Pipe() - process.standardOutput = outputPipe + #expect(error == "Error: Not enough cards\n") + } - let errorPipe = Pipe() - process.standardError = errorPipe + /// Returns path to the built products directory. + var productsDirectory: URL { + #if os(macOS) + return Bundle(for: BundleMarker.self).bundleURL.deletingLastPathComponent() + #else + return Bundle.main.bundleURL + #endif + } - try process.run() - process.waitUntilExit() + private func execute(with arguments: [String] = []) throws -> ( + status: Int32, output: String?, error: String? + ) { + let process = Process() + process.executableURL = productsDirectory.appendingPathComponent("dealer") + process.arguments = arguments - let status = process.terminationStatus + let outputPipe = Pipe() + process.standardOutput = outputPipe - let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() - let output = String(data: outputData, encoding: .utf8) + let errorPipe = Pipe() + process.standardError = errorPipe - let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() - let error = String(data: errorData, encoding: .utf8) + try process.run() + process.waitUntilExit() - return (status, output, error) - } + let status = process.terminationStatus + + let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: outputData, encoding: .utf8) + + let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() + let error = String(data: errorData, encoding: .utf8) + + return (status, output, error) + } } // MARK: - -private extension Character { - var isPlayingCardSuit: Bool { - switch self { - case "♠︎", "♡", "♢", "♣︎": - return true - default: - return false - } +extension Character { + fileprivate var isPlayingCardSuit: Bool { + switch self { + case "♠︎", "♡", "♢", "♣︎": + return true + default: + return false } + } }