Improve error messages

This commit is contained in:
Andrew Naylor 2016-09-25 22:13:23 +01:00
parent 94f3cea428
commit ce51cea8c6
11 changed files with 95 additions and 67 deletions

View file

@ -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()
} }
} }

View file

@ -57,6 +57,6 @@ extension ISStoreAccount {
return account return account
} }
throw error ?? MASError(code: .signInError) throw error ?? MASError.signInFailed(error: nil)
} }
} }

View file

@ -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 {

View file

@ -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(())
} }

View file

@ -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))
} }
} }

View file

@ -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 {

View file

@ -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(())

View file

@ -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))
} }
} }
} }

View file

@ -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
}

View file

@ -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)
} }

View file

@ -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))
} }