Skip to content

Commit a20056f

Browse files
authored
Merge pull request #973 from rgoldberg/972-list-outdated-app-ids
Accept app IDs to filter `list` & `outdated` output
2 parents d9ad495 + dcb14e3 commit a20056f

28 files changed

+139
-110
lines changed

Sources/MAS/Commands/Account.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ extension MAS {
1414
abstract: "Output the Apple Account signed in to the Mac App Store"
1515
)
1616

17-
/// Runs the command.
1817
func run() async throws {
1918
try await MAS.run { try await run(printer: $0) }
2019
}

Sources/MAS/Commands/Config.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ extension MAS {
2222
@Flag(help: "Output as Markdown")
2323
var markdown = false
2424

25-
/// Runs the command.
2625
func run() async throws {
2726
try await MAS.run { await run(printer: $0) }
2827
}

Sources/MAS/Commands/Home.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ extension MAS {
2020
)
2121

2222
@OptionGroup
23-
var appIDsOptionGroup: AppIDsOptionGroup
23+
var requiredAppIDsOptionGroup: RequiredAppIDsOptionGroup
2424

25-
/// Runs the command.
2625
func run() async throws {
2726
try await run(searcher: ITunesSearchAppStoreSearcher())
2827
}
@@ -32,17 +31,13 @@ extension MAS {
3231
}
3332

3433
private func run(printer: Printer, searcher: AppStoreSearcher) async {
35-
for appID in appIDsOptionGroup.appIDs {
36-
do {
37-
let result = try await searcher.lookup(appID: appID)
38-
guard let url = URL(string: result.trackViewUrl) else {
39-
throw MASError.urlParsing(result.trackViewUrl)
40-
}
41-
42-
try await url.open()
43-
} catch {
44-
printer.error(error: error)
34+
await requiredAppIDsOptionGroup.forEachAppID(printer: printer) { appID in
35+
let urlString = try await searcher.lookup(appID: appID).trackViewUrl
36+
guard let url = URL(string: urlString) else {
37+
throw MASError.urlParsing(urlString)
4538
}
39+
40+
try await url.open()
4641
}
4742
}
4843
}

Sources/MAS/Commands/Info.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ extension MAS {
2020
)
2121

2222
@OptionGroup
23-
var appIDsOptionGroup: AppIDsOptionGroup
23+
var requiredAppIDsOptionGroup: RequiredAppIDsOptionGroup
2424

25-
/// Runs the command.
2625
func run() async throws {
2726
try await run(searcher: ITunesSearchAppStoreSearcher())
2827
}
@@ -33,13 +32,8 @@ extension MAS {
3332

3433
private func run(printer: Printer, searcher: AppStoreSearcher) async {
3534
var spacing = ""
36-
for appID in appIDsOptionGroup.appIDs {
37-
do {
38-
printer.info("", AppInfoFormatter.format(app: try await searcher.lookup(appID: appID)), separator: spacing)
39-
} catch {
40-
printer.log(spacing, to: .standardError)
41-
printer.error(error: error)
42-
}
35+
await requiredAppIDsOptionGroup.forEachAppID(printer: printer) { appID in
36+
printer.info("", AppInfoFormatter.format(app: try await searcher.lookup(appID: appID)), separator: spacing)
4337
spacing = "\n"
4438
}
4539
}

Sources/MAS/Commands/Install.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,16 @@ extension MAS {
1717
@OptionGroup
1818
var forceOptionGroup: ForceOptionGroup
1919
@OptionGroup
20-
var appIDsOptionGroup: AppIDsOptionGroup
20+
var requiredAppIDsOptionGroup: RequiredAppIDsOptionGroup
2121

22-
/// Runs the command.
2322
func run() async throws {
2423
try await run(installedApps: await installedApps, searcher: ITunesSearchAppStoreSearcher())
2524
}
2625

2726
func run(installedApps: [InstalledApp], searcher: AppStoreSearcher) async throws {
2827
try await MAS.run { printer in
2928
await Downloader(printer: printer).downloadApps(
30-
withAppIDs: appIDsOptionGroup.appIDs,
29+
withAppIDs: requiredAppIDsOptionGroup.appIDs,
3130
purchasing: false,
3231
forceDownload: forceOptionGroup.force,
3332
installedApps: installedApps,

Sources/MAS/Commands/List.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ extension MAS {
1414
abstract: "List all apps installed from the Mac App Store"
1515
)
1616

17-
/// Runs the command.
17+
@OptionGroup
18+
var optionalAppIDsOptionGroup: OptionalAppIDsOptionGroup
19+
1820
func run() async throws {
1921
try run(installedApps: await installedApps)
2022
}
@@ -24,6 +26,7 @@ extension MAS {
2426
}
2527

2628
private func run(printer: Printer, installedApps: [InstalledApp]) {
29+
let installedApps = installedApps.filter(by: optionalAppIDsOptionGroup, printer: printer)
2730
if installedApps.isEmpty {
2831
printer.warning(
2932
"""

Sources/MAS/Commands/Lucky.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ extension MAS {
2525
@OptionGroup
2626
var searchTermOptionGroup: SearchTermOptionGroup
2727

28-
/// Runs the command.
2928
func run() async throws {
3029
try await run(installedApps: await installedApps, searcher: ITunesSearchAppStoreSearcher())
3130
}
@@ -38,12 +37,10 @@ extension MAS {
3837

3938
private func run(downloader: Downloader, installedApps: [InstalledApp], searcher: AppStoreSearcher) async throws {
4039
let searchTerm = searchTermOptionGroup.searchTerm
41-
guard let result = try await searcher.search(for: searchTerm).first else {
40+
guard let adamID = try await searcher.search(for: searchTerm).first?.adamID else {
4241
throw MASError.noSearchResultsFound(for: searchTerm)
4342
}
4443

45-
let adamID = result.adamID
46-
4744
if let installedApp = installedApps.first(where: { $0.adamID == adamID }), !forceOptionGroup.force {
4845
downloader.printer.warning(
4946
"Already installed: ",
File renamed without changes.

Sources/MAS/Commands/Open.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ extension MAS {
2323
abstract: "Open app page in 'App Store.app'"
2424
)
2525

26-
@Flag(name: .customLong("bundle"), help: ArgumentHelp("Process all app IDs as bundle IDs"))
27-
var forceBundleID = false
26+
@OptionGroup
27+
var forceBundleIDOptionGroup: ForceBundleIDOptionGroup
2828
@Argument(help: ArgumentHelp("App ID", valueName: "app-id"))
2929
var appIDString: String?
3030

31-
/// Runs the command.
3231
func run() async throws {
3332
try await run(searcher: ITunesSearchAppStoreSearcher())
3433
}
@@ -45,7 +44,7 @@ extension MAS {
4544
}
4645

4746
try await openInMacAppStore(
48-
pageForAppID: AppID(from: appIDString, forceBundleID: forceBundleID),
47+
pageForAppID: AppID(from: appIDString, forceBundleID: forceBundleIDOptionGroup.forceBundleID),
4948
searcher: searcher
5049
)
5150
}
@@ -64,14 +63,12 @@ private func openMacAppStore() async throws {
6463
}
6564

6665
private func openInMacAppStore(pageForAppID appID: AppID, searcher: AppStoreSearcher) async throws {
67-
let result = try await searcher.lookup(appID: appID)
68-
69-
guard var urlComponents = URLComponents(string: result.trackViewUrl) else {
70-
throw MASError.urlParsing(result.trackViewUrl)
66+
let urlString = try await searcher.lookup(appID: appID).trackViewUrl
67+
guard var urlComponents = URLComponents(string: urlString) else {
68+
throw MASError.urlParsing(urlString)
7169
}
7270

7371
urlComponents.scheme = masScheme
74-
7572
guard let url = urlComponents.url else {
7673
throw MASError.urlParsing(String(describing: urlComponents))
7774
}

Sources/MAS/Commands/OptionGroups/AppIDsOptionGroup.swift

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,43 @@
77

88
internal import ArgumentParser
99

10-
struct AppIDsOptionGroup: ParsableArguments {
11-
@Flag(name: .customLong("bundle"), help: ArgumentHelp("Process all app IDs as bundle IDs"))
12-
var forceBundleID = false
13-
@Argument(help: ArgumentHelp("App ID", valueName: "app-id"))
14-
var appIDStrings: [String]
10+
protocol AppIDsOptionGroup: ParsableArguments {
11+
var forceBundleIDOptionGroup: ForceBundleIDOptionGroup { get }
12+
var forceBundleID: Bool { get }
13+
var appIDStrings: [String] { get }
14+
var appIDs: [AppID] { get }
15+
}
16+
17+
extension AppIDsOptionGroup {
18+
var forceBundleID: Bool {
19+
forceBundleIDOptionGroup.forceBundleID
20+
}
1521

1622
var appIDs: [AppID] {
17-
appIDStrings.compactMap { AppID(from: $0, forceBundleID: forceBundleID) }
23+
appIDStrings.map { AppID(from: $0, forceBundleID: forceBundleID) }
24+
}
25+
26+
func forEachAppID(printer: Printer, _ body: (AppID) async throws -> Void) async {
27+
for appID in appIDs {
28+
do {
29+
try await body(appID)
30+
} catch {
31+
printer.error(error: error)
32+
}
33+
}
34+
}
35+
}
36+
37+
extension Array where Element: AppIdentifying {
38+
func filter(by appIDsOptionGroup: any AppIDsOptionGroup, printer: Printer) -> [Element] {
39+
appIDsOptionGroup.appIDStrings.isEmpty
40+
? self // swiftformat:disable:this indent
41+
: appIDsOptionGroup.appIDs.flatMap { appID in
42+
let appIdentifyings = filter { appID.matches($0) }
43+
if appIdentifyings.isEmpty {
44+
printer.error(appID.notInstalledMessage)
45+
}
46+
return appIdentifyings
47+
}
1848
}
1949
}

0 commit comments

Comments
 (0)