Improve spacing.

Partial #592

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-21 07:12:32 -04:00
parent 3eaffa5c3e
commit 71fbe2e444
No known key found for this signature in database
13 changed files with 75 additions and 59 deletions

View file

@ -20,19 +20,23 @@ import StoreFoundation
/// the promise is rejected with the first error, after all remaining downloads are attempted. /// the promise is rejected with the first error, after all remaining downloads are attempted.
func downloadAll(_ appIDs: [AppID], purchase: Bool = false) -> Promise<Void> { func downloadAll(_ appIDs: [AppID], purchase: Bool = false) -> Promise<Void> {
var firstError: Error? var firstError: Error?
return appIDs.reduce(Guarantee.value(())) { previous, appID in return
previous.then { appIDs
downloadWithRetries(appID, purchase: purchase).recover { error in .reduce(Guarantee.value(())) { previous, appID in
if firstError == nil { previous.then {
firstError = error downloadWithRetries(appID, purchase: purchase)
} .recover { error in
if firstError == nil {
firstError = error
}
}
} }
} }
}.done { .done {
if let error = firstError { if let error = firstError {
throw error throw error
}
} }
}
} }
private func downloadWithRetries(_ appID: AppID, purchase: Bool = false, attempts: Int = 3) -> Promise<Void> { private func downloadWithRetries(_ appID: AppID, purchase: Bool = false, attempts: Int = 3) -> Promise<Void> {

View file

@ -15,13 +15,15 @@ extension ISStoreAccount: StoreAccount {
if #available(macOS 10.13, *) { if #available(macOS 10.13, *) {
return race( return race(
Promise { seal in Promise { seal in
ISServiceProxy.genericShared().accountService.primaryAccount { storeAccount in ISServiceProxy.genericShared().accountService
seal.fulfill(storeAccount) .primaryAccount { storeAccount in
} seal.fulfill(storeAccount)
}
}, },
after(seconds: 30).then { after(seconds: 30)
Promise(error: MASError.notSignedIn) .then {
} Promise(error: MASError.notSignedIn)
}
) )
} else { } else {
return .value(CKAccountStore.shared().primaryAccount) return .value(CKAccountStore.shared().primaryAccount)
@ -76,9 +78,10 @@ extension ISStoreAccount: StoreAccount {
return race( return race(
signInPromise, signInPromise,
after(seconds: 30).then { after(seconds: 30)
Promise(error: MASError.signInFailed(error: nil)) .then {
} Promise(error: MASError.signInFailed(error: nil))
}
) )
} }
} }

View file

@ -9,7 +9,8 @@
import CommerceKit import CommerceKit
import StoreFoundation import StoreFoundation
@objc class PurchaseDownloadObserver: NSObject, CKDownloadQueueObserver { @objc
class PurchaseDownloadObserver: NSObject, CKDownloadQueueObserver {
let purchase: SSPurchase let purchase: SSPurchase
var completionHandler: (() -> Void)? var completionHandler: (() -> Void)?
var errorHandler: ((MASError) -> Void)? var errorHandler: ((MASError) -> Void)?

View file

@ -23,7 +23,6 @@ extension SSPurchase {
if purchase { if purchase {
parameters["macappinstalledconfirmed"] = 1 parameters["macappinstalledconfirmed"] = 1
parameters["pricingParameters"] = "STDQ" parameters["pricingParameters"] = "STDQ"
} else { } else {
parameters["pricingParameters"] = "STDRDL" parameters["pricingParameters"] = "STDRDL"
} }
@ -63,19 +62,20 @@ extension SSPurchase {
private func perform() -> Promise<Void> { private func perform() -> Promise<Void> {
Promise<SSPurchase> { seal in Promise<SSPurchase> { seal in
CKPurchaseController.shared().perform(self, withOptions: 0) { purchase, _, error, response in CKPurchaseController.shared()
if let error { .perform(self, withOptions: 0) { purchase, _, error, response in
seal.reject(MASError.purchaseFailed(error: error as NSError?)) if let error {
return seal.reject(MASError.purchaseFailed(error: error as NSError?))
} return
}
guard response?.downloads.isEmpty == false, let purchase else { guard response?.downloads.isEmpty == false, let purchase else {
seal.reject(MASError.noDownloads) seal.reject(MASError.noDownloads)
return return
} }
seal.fulfill(purchase) seal.fulfill(purchase)
} }
} }
.then { purchase in .then { purchase in
let observer = PurchaseDownloadObserver(purchase: purchase) let observer = PurchaseDownloadObserver(purchase: purchase)

View file

@ -35,13 +35,11 @@ extension Mas {
return return
} }
guard let result = try storeSearch.lookup(appID: appID).wait() guard let result = try storeSearch.lookup(appID: appID).wait() else {
else {
throw MASError.noSearchResultsFound throw MASError.noSearchResultsFound
} }
guard var url = URLComponents(string: result.trackViewUrl) guard var url = URLComponents(string: result.trackViewUrl) else {
else {
throw MASError.searchFailed throw MASError.searchFailed
} }
url.scheme = masScheme url.scheme = masScheme

View file

@ -32,7 +32,8 @@ extension Mas {
appLibrary.installedApps.map { installedApp in appLibrary.installedApps.map { installedApp in
firstly { firstly {
storeSearch.lookup(appID: installedApp.itemIdentifier.appIDValue) storeSearch.lookup(appID: installedApp.itemIdentifier.appIDValue)
}.done { storeApp in }
.done { storeApp in
guard let storeApp else { guard let storeApp else {
if verbose { if verbose {
printWarning( printWarning(

View file

@ -41,7 +41,8 @@ extension Mas {
print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):") print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):")
print( print(
apps.map { "\($0.installedApp.appName) (\($0.installedApp.bundleVersion)) -> (\($0.storeApp.version))" } apps.map { "\($0.installedApp.appName) (\($0.installedApp.bundleVersion)) -> (\($0.storeApp.version))" }
.joined(separator: "\n")) .joined(separator: "\n")
)
do { do {
try downloadAll(apps.map(\.installedApp.itemIdentifier.appIDValue)).wait() try downloadAll(apps.map(\.installedApp.itemIdentifier.appIDValue)).wait()
@ -71,7 +72,8 @@ extension Mas {
// only upgrade apps whose local version differs from the store version // only upgrade apps whose local version differs from the store version
firstly { firstly {
storeSearch.lookup(appID: installedApp.itemIdentifier.appIDValue) storeSearch.lookup(appID: installedApp.itemIdentifier.appIDValue)
}.map { result -> (SoftwareProduct, SearchResult)? in }
.map { result -> (SoftwareProduct, SearchResult)? in
guard let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) else { guard let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) else {
return nil return nil
} }

View file

@ -26,13 +26,13 @@ extension Mas {
func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws { func run(storeSearch: StoreSearch, openCommand: ExternalCommand) throws {
do { do {
guard let result = try storeSearch.lookup(appID: appID).wait() guard let result = try storeSearch.lookup(appID: appID).wait() else {
else {
throw MASError.noSearchResultsFound throw MASError.noSearchResultsFound
} }
guard let vendorWebsite = result.sellerUrl guard let vendorWebsite = result.sellerUrl else {
else { throw MASError.noVendorWebsite } throw MASError.noVendorWebsite
}
do { do {
try openCommand.run(arguments: vendorWebsite) try openCommand.run(arguments: vendorWebsite)

View file

@ -14,9 +14,10 @@ class MasAppLibrary: AppLibrary {
private let softwareMap: SoftwareMap private let softwareMap: SoftwareMap
/// Array of installed software products. /// Array of installed software products.
lazy var installedApps: [SoftwareProduct] = softwareMap.allSoftwareProducts().filter { product in lazy var installedApps: [SoftwareProduct] = softwareMap.allSoftwareProducts()
product.bundlePath.starts(with: "/Applications/") .filter { product in
} product.bundlePath.starts(with: "/Applications/")
}
/// Internal initializer for providing a mock software map. /// Internal initializer for providing a mock software map.
/// - Parameter softwareMap: SoftwareMap to use /// - Parameter softwareMap: SoftwareMap to use

View file

@ -53,9 +53,11 @@ class MasStoreSearch: StoreSearch {
// Combine the results, removing any duplicates. // Combine the results, removing any duplicates.
var seenAppIDs = Set<AppID>() var seenAppIDs = Set<AppID>()
return when(fulfilled: results).flatMapValues { $0 }.filterValues { result in return when(fulfilled: results)
seenAppIDs.insert(result.trackId).inserted .flatMapValues { $0 }
} .filterValues { result in
seenAppIDs.insert(result.trackId).inserted
}
} }
/// Looks up app details. /// Looks up app details.
@ -75,14 +77,14 @@ class MasStoreSearch: StoreSearch {
return .value(nil) return .value(nil)
} }
guard let pageUrl = URL(string: result.trackViewUrl) guard let pageUrl = URL(string: result.trackViewUrl) else {
else {
return .value(result) return .value(result)
} }
return firstly { return firstly {
self.scrapeAppStoreVersion(pageUrl) self.scrapeAppStoreVersion(pageUrl)
}.map { pageVersion in }
.map { pageVersion in
guard let pageVersion, guard let pageVersion,
let searchVersion = Version(tolerant: result.version), let searchVersion = Version(tolerant: result.version),
pageVersion > searchVersion pageVersion > searchVersion
@ -94,7 +96,8 @@ class MasStoreSearch: StoreSearch {
var result = result var result = result
result.version = pageVersion.description result.version = pageVersion.description
return result return result
}.recover { _ in }
.recover { _ in
// If we were unable to scrape the App Store page, assume compatibility. // If we were unable to scrape the App Store page, assume compatibility.
.value(result) .value(result)
} }
@ -120,7 +123,8 @@ class MasStoreSearch: StoreSearch {
private func scrapeAppStoreVersion(_ pageUrl: URL) -> Promise<Version?> { private func scrapeAppStoreVersion(_ pageUrl: URL) -> Promise<Version?> {
firstly { firstly {
networkManager.loadData(from: pageUrl) networkManager.loadData(from: pageUrl)
}.map { data in }
.map { data in
guard let html = String(data: data, encoding: .utf8), guard let html = String(data: data, encoding: .utf8),
let capture = MasStoreSearch.appVersionExpression.firstMatch(in: html)?.captures[0], let capture = MasStoreSearch.appVersionExpression.firstMatch(in: html)?.captures[0],
let version = Version(tolerant: capture) let version = Version(tolerant: capture)

View file

@ -18,7 +18,9 @@ private var standardError = FileHandle.standardError
extension FileHandle: TextOutputStream { extension FileHandle: TextOutputStream {
/// Appends the given string to the stream. /// Appends the given string to the stream.
public func write(_ string: String) { public func write(_ string: String) {
guard let data = string.data(using: .utf8) else { return } guard let data = string.data(using: .utf8) else {
return
}
write(data) write(data)
} }
} }

View file

@ -18,8 +18,7 @@ class StoreSearchMock: StoreSearch {
} }
func lookup(appID: AppID) -> Promise<SearchResult?> { func lookup(appID: AppID) -> Promise<SearchResult?> {
guard let result = apps[appID] guard let result = apps[appID] else {
else {
return Promise(error: MASError.noSearchResultsFound) return Promise(error: MASError.noSearchResultsFound)
} }

View file

@ -22,8 +22,9 @@ class NetworkSessionMockFromFile: NetworkSessionMock {
} }
override func loadData(from _: URL) -> Promise<Data> { override func loadData(from _: URL) -> Promise<Data> {
guard let fileURL = Bundle.url(for: responseFile) guard let fileURL = Bundle.url(for: responseFile) else {
else { fatalError("Unable to load file \(responseFile)") } fatalError("Unable to load file \(responseFile)")
}
do { do {
return .value(try Data(contentsOf: fileURL, options: .mappedIfSafe)) return .value(try Data(contentsOf: fileURL, options: .mappedIfSafe))