Skip to content

Support optional JSON output for config, list, lookup/info, outdated & search; improve tabular output#1230

Open
rgoldberg wants to merge 3 commits intomas-cli:mainfrom
rgoldberg:7.0.0/190-json
Open

Support optional JSON output for config, list, lookup/info, outdated & search; improve tabular output#1230
rgoldberg wants to merge 3 commits intomas-cli:mainfrom
rgoldberg:7.0.0/190-json

Conversation

@rgoldberg
Copy link
Copy Markdown
Member

Support optional JSON output for config, list, lookup/info, outdated & search; improve tabular output.

Resolve #190
Resolve #412
Resolve #1091

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
@rgoldberg rgoldberg added this to the 7.0.0 milestone Apr 22, 2026
@rgoldberg rgoldberg self-assigned this Apr 22, 2026
@rgoldberg rgoldberg force-pushed the 7.0.0/190-json branch 5 times, most recently from ecf7ab1 to b8c8eb5 Compare April 22, 2026 04:11
…outdated` & `search`.

Update `README.md`.

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces native JSON output support for the list, outdated, search, lookup, and config commands. This is achieved through the integration of a new swift-json library and a Zsh wrapper script that uses jq and column for terminal formatting. The implementation includes a significant refactor of the CatalogApp and InstalledApp models to handle metadata key mapping and JSON serialization. Feedback highlights several performance and architectural concerns, including inefficient JSON re-parsing in the outdated command, high memory overhead from storing raw JSON strings in models, and potential performance degradation due to disk I/O during canonical path resolution. Additionally, the review points out the tight coupling created by using a magic file descriptor (FD 3) for communication between the binary and the wrapper script, and the bypass of terminal detection logic via hardcoded ANSI escape sequences.

Comment thread Scripts/mas
"(" + $version + " " * ($max_version_length - ($version | length)) + " -> " + .newVersion + ")"
] |
join("\u001f")
) catch ("\u001B[4;31mError:\u001B[0m Invalid data from mas: \(.)\n" | halt_error(1))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding ANSI escape sequences for error formatting in the jq script bypasses the terminal detection logic in the Swift Printer class. If the output is redirected to a file, these escape codes will be included as literal characters, which is undesirable for non-interactive use.

Comment thread Sources/mas/Commands/OptionGroups/OutputFormatOptionGroup.swift
outdatedApps.compactMap { installedApp, newVersion in
do {
let newVersionKey = "newVersion"
var json = try JSON.Object(parsing: .init(describing: installedApp))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing the JSON string back into a JSON.Object just to insert a single field is inefficient, especially when processing a large list of outdated apps. Since InstalledApp already computes and stores its JSON representation, consider exposing the JSON.Object directly or providing a method to extend it without full re-parsing.

References
  1. Avoid unnecessary loops, iterations, or calculations. Identify performance bottlenecks and optimize for efficiency.

var displayPrice: String {
formattedPrice ?? "?"
private let jsonObject: JSON.Object
private let json: String
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Storing the full JSON representation as a String in every CatalogApp instance increases memory consumption, particularly for large search results. Consider making this a computed property or generating it on demand to reduce the memory footprint of the model.

}

fileprivate init(for item: NSMetadataItem) {
let valueByAttribute = item.values(forAttributes: item.attributes + [NSMetadataItemPathKey]) ?? .init()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Fetching all attributes via item.attributes for every item in the Spotlight query results can be very slow. It is more efficient to specify only the required attributes in the values(forAttributes:) call.

References
  1. Identify performance bottlenecks and optimize for efficiency.

Comment on lines +35 to +36
return (try? URL(filePath: path, directoryHint: .isDirectory).resourceValues(forKeys: [.canonicalPathKey]))?
.canonicalPath // swiftformat:disable:this indent
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Resolving the canonicalPath involves disk I/O for every installed app found. This will significantly degrade the performance of the list command when many apps are installed. Consider if this resolution is strictly necessary for the default output.

@mas-cli mas-cli deleted a comment from gemini-code-assist Bot Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create libexec setup script Output column misalignment for certain characters JSON output option

1 participant