Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .periphery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# .periphery.yaml
# mas
#
# Periphery 3.7.2
# Periphery 3.7.4
#
---
color: always
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ Do NOT refactor code if doing so makes the caller interface worse. Specifically:
verbosity, introduces duplication bugs & makes code harder to maintain. Keep
clean abstractions. Example of what NOT to do:
```swift
// ❌ BAD: Inlining capitalizingFirstCharacter at multiple call sites
// ❌ BAD: Inlining uppercasingFirst at multiple call sites
action1.performing.prefix(1).uppercased() + action.performing.dropFirst()
action2.performing.prefix(1).uppercased() + action.performing.dropFirst()
// ✅ GOOD: Use the utility function
action1.performing.capitalizingFirstCharacter
action2.performing.capitalizingFirstCharacter
action1.performing.uppercasingFirst
action2.performing.uppercasingFirst
```
- **Never replace a clean, readable abstraction with a verbose closure**. e.g.,
if a custom `SortComparator` or similar is used multiple times, keep it.
Expand Down
2 changes: 1 addition & 1 deletion Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ brew "gh" # 2.91.0
brew "git" # 2.54.0
brew "ipsw" # 3.1.672
brew "markdownlint-cli2" # 0.22.1
brew "periphery" if MacOS.version >= :sequoia && `/usr/bin/arch` == "arm64" # 3.7.2
brew "periphery" if MacOS.version >= :sequoia && `/usr/bin/arch` == "arm64" # 3.7.4
brew "shellcheck" # 0.11.0
brew "swiftformat" # 0.61.0
brew "swiftlint" # 0.63.2
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,29 +141,31 @@ block for each app, with a blank line between apps.
If `--json` is supplied, these commands output a stream of JSON objects—one per
app—each containing all fields provided by Apple for that app.

Many of the JSON keys provided by Apple are poorly named, so they are mapped to
Many of the keys provided by Apple are poorly named, so they are mapped to
better names by an algorithm.

<!--editorconfig-checker-disable-->
Mapped JSON keys are [sorted](
Mapped keys are [sorted](
https://developer.apple.com/documentation/foundation/nsstring/compareoptions/numeric
).
<!--editorconfig-checker-enable-->

Each JSON key should be unique within an object; if duplicate keys exist in an
Each key should be unique within an object; if duplicate keys exist in an
object, their relative ordering in the input is preserved in the output.

If Apple renames or adds JSON keys, suboptimal JSON keys might be output until
the mapping is updated.
For tabular output, if an object contains duplicate keys, the last value is
used.

If Apple renames or adds keys, suboptimal keys might be output until the mapping
is updated.

## JSON Config Output

`config` normally outputs settings as key-value pairs, one per line.

If `--json` is supplied, `config` outputs all settings in a single JSON object.

Since the JSON keys are defined by mas, they are guaranteed to be unique &
correct.
Since the keys are defined by mas, they are guaranteed to be unique & correct.

## Spotlight

Expand Down
43 changes: 16 additions & 27 deletions Scripts/mas
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,33 @@ readonly jq

[[ -t 2 ]] && readonly error_prefix=$'\u001B[4;31mError:\u001B[0m' || readonly error_prefix=Error:

# shellcheck disable=SC1072,SC1073
case "${1:-}" in
outdated)
# shellcheck disable=SC1056,SC1072,SC1073
{
# shellcheck disable=SC1083
{ exec "${mas}" "${@}" 3>&1 1>&4 2>&5 } | "${jq}" -sr '
try (
(map(.adamID | tostring | length) | max) as $max_adam_id_length |
(map(.version // "" | length) | max) as $max_version_length |
.[] |
(.adamID | tostring) as $adam_id |
(.version // "") as $version |
[
" " * ($max_adam_id_length - ($adam_id | length)) + $adam_id,
.name,
"(" + $version + " " * ($max_version_length - ($version | length)) + " -> " + .newVersion + ")"
] |
join("\u001f")
) catch ("'"${error_prefix}"' Invalid data from mas: \(.)\n" | halt_error(1))
' | "${column}" -ts $'\u001f'
} 4>&1 5>&2
;;
readonly max_version_length_jq=$'\n\t(map(.version // "" | length) | max) as $max_version_length |'
readonly new_version_jq=$' + ($version | pad_spaces($max_version_length)) + " -> " + .newVersion'
;|
search)
[[ -n ${argv[(r)--price]-} ]] && readonly output_price=' + [.formattedPrice // .price // "?"]'
;&
list)
list|outdated)
# shellcheck disable=SC1056
{
{ exec "${mas}" "${@}" 3>&1 1>&4 2>&5 } |
"${jq}" -sr '
# shellcheck disable=SC1083
{ exec "${mas}" "${@}" 3>&1 1>&4 2>&5 } | "${jq}" -sr '
def pad_spaces($count):
" " * ($count - (. | length))
;
try (
(map(.adamID | tostring | length) | max) as $max_adam_id_length |
(map(.adamID | tostring | length) | max) as $max_adam_id_length |'"${max_version_length_jq-}"'
.[] |
(.adamID | tostring) as $adam_id |
(.version // "") as $version |
[
" " * ($max_adam_id_length - ($adam_id | length)) + $adam_id,
($adam_id | pad_spaces($max_adam_id_length)) + $adam_id,
.name,
"(" + (.version // "") + ")"
] '"${output_price-}"'|
"(" + $version'"${new_version_jq-}"' + ")"
]'"${output_price-}"' |
join("\u001f")
) catch ("'"${error_prefix}"' Invalid data from mas: \(.)\n" | halt_error(1))
' | "${column}" -ts $'\u001f'
Expand Down
10 changes: 5 additions & 5 deletions Sources/mas/AppStore/AppStoreAction+download.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ private actor DownloadQueueObserver: CKDownloadQueueObserver {
}

MAS.printer.notice(PhaseType.downloaded, snapshot.appNameAndVersion)
MAS.printer.notice(action.performing.capitalizingFirstCharacter, snapshot.appNameAndVersion)
MAS.printer.info(action.rawValue.capitalizingFirstCharacter, "progress cannot be displayed", terminator: "")
MAS.printer.notice(action.performing.uppercasingFirst, snapshot.appNameAndVersion)
MAS.printer.info(action.rawValue.uppercasingFirst, "progress cannot be displayed", terminator: "")
appFolderURL = try await install(appNameAndVersion: snapshot.appNameAndVersion)
MAS.printer.clearCurrentLine(of: .standardOutput)
} else {
Expand All @@ -286,7 +286,7 @@ private actor DownloadQueueObserver: CKDownloadQueueObserver {
}

MAS.printer.notice(
[action.performed.capitalizingFirstCharacter, snapshot.appNameAndVersion]
[action.performed.uppercasingFirst, snapshot.appNameAndVersion]
+ (appFolderURL.map { ["in", $0.filePath] } ?? .init()),
)

Expand All @@ -312,7 +312,7 @@ private actor DownloadQueueObserver: CKDownloadQueueObserver {
"Multiple installations of ",
snapshot.name ?? "unknown app",
" exist in the applications folders\n\n",
action.performed.capitalizingFirstCharacter,
action.performed.uppercasingFirst,
":\n",
appFolderPath,
"\n\nOthers:\n",
Expand All @@ -322,7 +322,7 @@ private actor DownloadQueueObserver: CKDownloadQueueObserver {
}
} else {
MAS.printer.warning(
action.performed.capitalizingFirstCharacter,
action.performed.uppercasingFirst,
snapshot.appNameAndVersion,
"outside of the applications folders, in",
appFolderURL.filePath,
Expand Down
2 changes: 1 addition & 1 deletion Sources/mas/Commands/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension MAS {
private func run(appStorePageURLStrings: [String]) async {
await appStorePageURLStrings.forEach(attemptTo: "open") { appStorePageURLString in
guard let url = URL(string: appStorePageURLString) else {
throw MASError.unparsableURL(appStorePageURLString)
throw MASError.invalidURL(appStorePageURLString)
}

_ = try await url.open()
Expand Down
4 changes: 2 additions & 2 deletions Sources/mas/Commands/Open.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ private func openMacAppStore() async throws {

private func openMacAppStorePage(forAppStorePageURLString appStorePageURLString: String) async throws {
guard var urlComponents = URLComponents(string: appStorePageURLString) else {
throw MASError.unparsableURL(appStorePageURLString)
throw MASError.invalidURL(appStorePageURLString)
}

urlComponents.scheme = masScheme
guard let url = urlComponents.url else {
throw MASError.unparsableURL(.init(describing: urlComponents))
throw MASError.invalidURL(.init(describing: urlComponents))
}

_ = try await url.open()
Expand Down
2 changes: 0 additions & 2 deletions Sources/mas/Commands/Outdated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
//

internal import ArgumentParser
private import JSON
private import JSONAST

extension MAS {
/// Outputs a list of installed apps which have updates available to be
Expand Down
2 changes: 1 addition & 1 deletion Sources/mas/Commands/Seller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension MAS {
private func run(sellerURLStrings: [String]) async {
await sellerURLStrings.forEach(attemptTo: "open") { sellerURLString in
guard let url = URL(string: sellerURLString) else {
throw MASError.unparsableURL(sellerURLString)
throw MASError.invalidURL(sellerURLString)
}

_ = try await url.open()
Expand Down
Loading
Loading