mirror of
https://github.com/mas-cli/mas
synced 2024-11-29 06:50:39 +00:00
Improve error messages
This commit is contained in:
parent
94f3cea428
commit
ce51cea8c6
11 changed files with 95 additions and 67 deletions
|
@ -9,7 +9,7 @@
|
||||||
func download(_ adamId: UInt64) -> MASError? {
|
func download(_ adamId: UInt64) -> MASError? {
|
||||||
|
|
||||||
guard let account = ISStoreAccount.primaryAccount else {
|
guard let account = ISStoreAccount.primaryAccount else {
|
||||||
return MASError(code: .notSignedIn)
|
return .notSignedIn
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
@ -21,7 +21,7 @@ func download(_ adamId: UInt64) -> MASError? {
|
||||||
group.enter()
|
group.enter()
|
||||||
purchase.perform { purchase, _, error, response in
|
purchase.perform { purchase, _, error, response in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
purchaseError = MASError(code: .purchaseError, sourceError: error as NSError)
|
purchaseError = .purchaseFailed(error: error as NSError?)
|
||||||
group.leave()
|
group.leave()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func download(_ adamId: UInt64) -> MASError? {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print("No downloads")
|
print("No downloads")
|
||||||
purchaseError = MASError(code: .noDownloads)
|
purchaseError = .noDownloads
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ extension ISStoreAccount {
|
||||||
return account
|
return account
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error ?? MASError(code: .signInError)
|
throw error ?? MASError.signInFailed(error: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Copyright (c) 2015 Andrew Naylor. All rights reserved.
|
// Copyright (c) 2015 Andrew Naylor. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
let csi = "\u{001B}["
|
|
||||||
|
|
||||||
@objc class PurchaseDownloadObserver: NSObject, CKDownloadQueueObserver {
|
@objc class PurchaseDownloadObserver: NSObject, CKDownloadQueueObserver {
|
||||||
let purchase: SSPurchase
|
let purchase: SSPurchase
|
||||||
var completionHandler: (() -> ())?
|
var completionHandler: (() -> ())?
|
||||||
|
@ -36,7 +34,7 @@ let csi = "\u{001B}["
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clearLine()
|
clearLine()
|
||||||
print("==> Downloading " + download.metadata.title)
|
printInfo("Downloading \(download.metadata.title)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadQueue(_ queue: CKDownloadQueue, changedWithRemoval download: SSDownload!) {
|
func downloadQueue(_ queue: CKDownloadQueue, changedWithRemoval download: SSDownload!) {
|
||||||
|
@ -47,15 +45,13 @@ let csi = "\u{001B}["
|
||||||
|
|
||||||
clearLine()
|
clearLine()
|
||||||
if status.isFailed {
|
if status.isFailed {
|
||||||
print("==> Download Failed")
|
errorHandler?(.downloadFailed(error: status.error as NSError?))
|
||||||
errorHandler?(MASError(code: .downloadFailed, sourceError: status.error as NSError?))
|
|
||||||
}
|
}
|
||||||
else if status.isCancelled {
|
else if status.isCancelled {
|
||||||
print("==> Download Cancelled")
|
errorHandler?(.cancelled)
|
||||||
errorHandler?(MASError(code: .cancelled))
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print("==> Installed " + download.metadata.title)
|
printInfo("Installed \(download.metadata.title)")
|
||||||
completionHandler?()
|
completionHandler?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,14 +89,6 @@ func progress(_ state: ProgressState) {
|
||||||
fflush(stdout)
|
fflush(stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearLine() {
|
|
||||||
guard isatty(fileno(stdout)) != 0 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
print("\(csi)2K\(csi)0G", terminator: "")
|
|
||||||
fflush(stdout)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SSDownloadStatus {
|
extension SSDownloadStatus {
|
||||||
var progressState: ProgressState? {
|
var progressState: ProgressState? {
|
||||||
if let phase = activePhase {
|
if let phase = activePhase {
|
||||||
|
|
|
@ -17,7 +17,7 @@ struct AccountCommand: CommandProtocol {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print("Not signed in")
|
print("Not signed in")
|
||||||
return .failure(MASError(code: .notSignedIn))
|
return .failure(.notSignedIn)
|
||||||
}
|
}
|
||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct InstallCommand: CommandProtocol {
|
||||||
// Try to download applications with given identifiers and collect results
|
// Try to download applications with given identifiers and collect results
|
||||||
let downloadResults = options.appIds.flatMap { (appId) -> MASError? in
|
let downloadResults = options.appIds.flatMap { (appId) -> MASError? in
|
||||||
if let product = installedApp(appId) , !options.forceInstall {
|
if let product = installedApp(appId) , !options.forceInstall {
|
||||||
warn("\(product.appName) is already installed")
|
printWarning("\(product.appName) is already installed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ struct InstallCommand: CommandProtocol {
|
||||||
case 1:
|
case 1:
|
||||||
return .failure(downloadResults[0])
|
return .failure(downloadResults[0])
|
||||||
default:
|
default:
|
||||||
return .failure(MASError(code: .downloadFailed))
|
return .failure(.downloadFailed(error: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,13 @@ struct SearchCommand: CommandProtocol {
|
||||||
|
|
||||||
guard let searchURLString = searchURLString(options.appName),
|
guard let searchURLString = searchURLString(options.appName),
|
||||||
let searchJson = URLSession.requestSynchronousJSONWithURLString(searchURLString) as? [String: AnyObject] else {
|
let searchJson = URLSession.requestSynchronousJSONWithURLString(searchURLString) as? [String: AnyObject] else {
|
||||||
return .failure(MASError(code:.searchError))
|
return .failure(.searchFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let resultCount = searchJson[ResultKeys.ResultCount] as? Int , resultCount > 0,
|
guard let resultCount = searchJson[ResultKeys.ResultCount] as? Int , resultCount > 0,
|
||||||
let results = searchJson[ResultKeys.Results] as? [[String: AnyObject]] else {
|
let results = searchJson[ResultKeys.Results] as? [[String: AnyObject]] else {
|
||||||
print("No results found")
|
print("No results found")
|
||||||
return .failure(MASError(code:.noSearchResultsFound))
|
return .failure(.noSearchResultsFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
for result in results {
|
for result in results {
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct SignInCommand: CommandProtocol {
|
||||||
func run(_ options: Options) -> Result<(), MASError> {
|
func run(_ options: Options) -> Result<(), MASError> {
|
||||||
|
|
||||||
guard ISStoreAccount.primaryAccount == nil else {
|
guard ISStoreAccount.primaryAccount == nil else {
|
||||||
return .failure(MASError.init(code: .alreadySignedIn))
|
return .failure(.alreadySignedIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -29,7 +29,7 @@ struct SignInCommand: CommandProtocol {
|
||||||
|
|
||||||
let _ = try ISStoreAccount.signIn(username: options.username, password: password, systemDialog: options.dialog)
|
let _ = try ISStoreAccount.signIn(username: options.username, password: password, systemDialog: options.dialog)
|
||||||
} catch let error as NSError {
|
} catch let error as NSError {
|
||||||
return .failure(MASError(code: .signInError, sourceError: error))
|
return .failure(.signInFailed(error: error))
|
||||||
}
|
}
|
||||||
|
|
||||||
return .success(())
|
return .success(())
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct UpgradeCommand: CommandProtocol {
|
||||||
updates = appIds.flatMap { updateController?.availableUpdate(withItemIdentifier: $0) }
|
updates = appIds.flatMap { updateController?.availableUpdate(withItemIdentifier: $0) }
|
||||||
|
|
||||||
guard updates.count > 0 else {
|
guard updates.count > 0 else {
|
||||||
warn("Nothing found to upgrade")
|
printWarning("Nothing found to upgrade")
|
||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,7 +46,7 @@ struct UpgradeCommand: CommandProtocol {
|
||||||
case 1:
|
case 1:
|
||||||
return .failure(updateResults[0])
|
return .failure(updateResults[0])
|
||||||
default:
|
default:
|
||||||
return .failure(MASError(code: .downloadFailed))
|
return .failure(.downloadFailed(error: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,39 +6,58 @@
|
||||||
// Copyright (c) 2015 Andrew Naylor. All rights reserved.
|
// Copyright (c) 2015 Andrew Naylor. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
public let MASErrorDomain: String = "MASErrorDomain"
|
enum MASError: Error, CustomStringConvertible {
|
||||||
|
|
||||||
private let MASErrorSource: String = "MASErrorSource"
|
|
||||||
|
|
||||||
public enum MASErrorCode: Int {
|
|
||||||
case noError
|
|
||||||
case notSignedIn
|
case notSignedIn
|
||||||
case purchaseError
|
case signInFailed(error: NSError?)
|
||||||
|
case alreadySignedIn
|
||||||
|
|
||||||
|
case purchaseFailed(error: NSError?)
|
||||||
|
case downloadFailed(error: NSError?)
|
||||||
case noDownloads
|
case noDownloads
|
||||||
case cancelled
|
case cancelled
|
||||||
case downloadFailed
|
|
||||||
case signInError
|
case searchFailed
|
||||||
case alreadySignedIn
|
|
||||||
case searchError
|
|
||||||
case noSearchResultsFound
|
case noSearchResultsFound
|
||||||
case noUpdatesFound
|
|
||||||
|
|
||||||
var exitCode: Int32 {
|
var description: String {
|
||||||
return Int32(self.rawValue)
|
switch self {
|
||||||
|
case .notSignedIn:
|
||||||
|
return "Not signed in"
|
||||||
|
|
||||||
|
case .signInFailed(error: let error):
|
||||||
|
if let error = error {
|
||||||
|
return "Sign in failed: \(error.localizedDescription)"
|
||||||
|
} else {
|
||||||
|
return "Sign in failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
case .alreadySignedIn:
|
||||||
|
return "Already signed in"
|
||||||
|
|
||||||
|
case .purchaseFailed(error: let error):
|
||||||
|
if let error = error {
|
||||||
|
return "Download request failed: \(error.localizedDescription)"
|
||||||
|
} else {
|
||||||
|
return "Download request failed"
|
||||||
|
}
|
||||||
|
case .downloadFailed(error: let error):
|
||||||
|
if let error = error {
|
||||||
|
return "Download failed: \(error.localizedDescription)"
|
||||||
|
} else {
|
||||||
|
return "Download failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
case .noDownloads:
|
||||||
|
return "No downloads began"
|
||||||
|
|
||||||
|
case .cancelled:
|
||||||
|
return "Download cancelled"
|
||||||
|
|
||||||
|
case .searchFailed:
|
||||||
|
return "Search failed"
|
||||||
|
|
||||||
|
case .noSearchResultsFound:
|
||||||
|
return "No results found"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class MASError: Error {
|
|
||||||
let code: MASErrorCode
|
|
||||||
|
|
||||||
let sourceError: NSError?
|
|
||||||
|
|
||||||
init(code: MASErrorCode, sourceError: NSError? = nil) {
|
|
||||||
self.code = code
|
|
||||||
self.sourceError = sourceError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func == (lhs: MASError, rhs: MASError) -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,22 +6,46 @@
|
||||||
// Copyright © 2016 Andrew Naylor. All rights reserved.
|
// Copyright © 2016 Andrew Naylor. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
func warn(_ message: String) {
|
/// A collection of output formatting helpers
|
||||||
|
|
||||||
|
|
||||||
|
/// Terminal Control Sequence Indicator
|
||||||
|
let csi = "\u{001B}["
|
||||||
|
|
||||||
|
func printInfo(_ message: String) {
|
||||||
|
guard isatty(fileno(stdout)) != 0 else {
|
||||||
|
print("==> \(message)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blue bold arrow, Bold text
|
||||||
|
print("\(csi)1;34m==>\(csi)0m \(csi)1m\(message)\(csi)0m")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printWarning(_ message: String) {
|
||||||
guard isatty(fileno(stdout)) != 0 else {
|
guard isatty(fileno(stdout)) != 0 else {
|
||||||
print("Warning: \(message)")
|
print("Warning: \(message)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yellow, underlined "Warning:" prefix
|
// Yellow, underlined "Warning:" prefix
|
||||||
print("\(csi)4m\(csi)33mWarning:\(csi)0m \(message)")
|
print("\(csi)4;33mWarning:\(csi)0m \(message)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func error(_ message: String) {
|
func printError(_ message: String) {
|
||||||
guard isatty(fileno(stdout)) != 0 else {
|
guard isatty(fileno(stdout)) != 0 else {
|
||||||
print("Warning: \(message)")
|
print("Warning: \(message)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Red, underlined "Error:" prefix
|
// Red, underlined "Error:" prefix
|
||||||
print("\(csi)4m\(csi)31mError:\(csi)0m \(message)")
|
print("\(csi)4;31mError:\(csi)0m \(message)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearLine() {
|
||||||
|
guard isatty(fileno(stdout)) != 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
print("\(csi)2K\(csi)0G", terminator: "")
|
||||||
|
fflush(stdout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,7 @@ registry.register(VersionCommand())
|
||||||
registry.register(helpCommand)
|
registry.register(helpCommand)
|
||||||
|
|
||||||
registry.main(defaultVerb: helpCommand.verb) { error in
|
registry.main(defaultVerb: helpCommand.verb) { error in
|
||||||
if let sourceError = error.sourceError {
|
printError(String(describing: error))
|
||||||
var stderr = StderrOutputStream()
|
exit(1)
|
||||||
print(sourceError.localizedDescription, to: &stderr)
|
|
||||||
}
|
|
||||||
exit(Int32(error.code.rawValue))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue