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? {
guard let account = ISStoreAccount.primaryAccount else {
return MASError(code: .notSignedIn)
return .notSignedIn
}
let group = DispatchGroup()
@ -21,7 +21,7 @@ func download(_ adamId: UInt64) -> MASError? {
group.enter()
purchase.perform { purchase, _, error, response in
if let error = error {
purchaseError = MASError(code: .purchaseError, sourceError: error as NSError)
purchaseError = .purchaseFailed(error: error as NSError?)
group.leave()
return
}
@ -43,7 +43,7 @@ func download(_ adamId: UInt64) -> MASError? {
}
else {
print("No downloads")
purchaseError = MASError(code: .noDownloads)
purchaseError = .noDownloads
group.leave()
}
}

View file

@ -57,6 +57,6 @@ extension ISStoreAccount {
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.
//
let csi = "\u{001B}["
@objc class PurchaseDownloadObserver: NSObject, CKDownloadQueueObserver {
let purchase: SSPurchase
var completionHandler: (() -> ())?
@ -36,7 +34,7 @@ let csi = "\u{001B}["
return
}
clearLine()
print("==> Downloading " + download.metadata.title)
printInfo("Downloading \(download.metadata.title)")
}
func downloadQueue(_ queue: CKDownloadQueue, changedWithRemoval download: SSDownload!) {
@ -47,15 +45,13 @@ let csi = "\u{001B}["
clearLine()
if status.isFailed {
print("==> Download Failed")
errorHandler?(MASError(code: .downloadFailed, sourceError: status.error as NSError?))
errorHandler?(.downloadFailed(error: status.error as NSError?))
}
else if status.isCancelled {
print("==> Download Cancelled")
errorHandler?(MASError(code: .cancelled))
errorHandler?(.cancelled)
}
else {
print("==> Installed " + download.metadata.title)
printInfo("Installed \(download.metadata.title)")
completionHandler?()
}
}
@ -93,14 +89,6 @@ func progress(_ state: ProgressState) {
fflush(stdout)
}
func clearLine() {
guard isatty(fileno(stdout)) != 0 else {
return
}
print("\(csi)2K\(csi)0G", terminator: "")
fflush(stdout)
}
extension SSDownloadStatus {
var progressState: ProgressState? {
if let phase = activePhase {

View file

@ -17,7 +17,7 @@ struct AccountCommand: CommandProtocol {
}
else {
print("Not signed in")
return .failure(MASError(code: .notSignedIn))
return .failure(.notSignedIn)
}
return .success(())
}

View file

@ -15,7 +15,7 @@ struct InstallCommand: CommandProtocol {
// Try to download applications with given identifiers and collect results
let downloadResults = options.appIds.flatMap { (appId) -> MASError? in
if let product = installedApp(appId) , !options.forceInstall {
warn("\(product.appName) is already installed")
printWarning("\(product.appName) is already installed")
return nil
}
@ -28,7 +28,7 @@ struct InstallCommand: CommandProtocol {
case 1:
return .failure(downloadResults[0])
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),
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,
let results = searchJson[ResultKeys.Results] as? [[String: AnyObject]] else {
print("No results found")
return .failure(MASError(code:.noSearchResultsFound))
return .failure(.noSearchResultsFound)
}
for result in results {

View file

@ -14,7 +14,7 @@ struct SignInCommand: CommandProtocol {
func run(_ options: Options) -> Result<(), MASError> {
guard ISStoreAccount.primaryAccount == nil else {
return .failure(MASError.init(code: .alreadySignedIn))
return .failure(.alreadySignedIn)
}
do {
@ -29,7 +29,7 @@ struct SignInCommand: CommandProtocol {
let _ = try ISStoreAccount.signIn(username: options.username, password: password, systemDialog: options.dialog)
} catch let error as NSError {
return .failure(MASError(code: .signInError, sourceError: error))
return .failure(.signInFailed(error: error))
}
return .success(())

View file

@ -20,7 +20,7 @@ struct UpgradeCommand: CommandProtocol {
updates = appIds.flatMap { updateController?.availableUpdate(withItemIdentifier: $0) }
guard updates.count > 0 else {
warn("Nothing found to upgrade")
printWarning("Nothing found to upgrade")
return .success(())
}
} else {
@ -46,7 +46,7 @@ struct UpgradeCommand: CommandProtocol {
case 1:
return .failure(updateResults[0])
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.
//
public let MASErrorDomain: String = "MASErrorDomain"
private let MASErrorSource: String = "MASErrorSource"
public enum MASErrorCode: Int {
case noError
enum MASError: Error, CustomStringConvertible {
case notSignedIn
case purchaseError
case signInFailed(error: NSError?)
case alreadySignedIn
case purchaseFailed(error: NSError?)
case downloadFailed(error: NSError?)
case noDownloads
case cancelled
case downloadFailed
case signInError
case alreadySignedIn
case searchError
case searchFailed
case noSearchResultsFound
case noUpdatesFound
var exitCode: Int32 {
return Int32(self.rawValue)
var description: String {
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.
//
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 {
print("Warning: \(message)")
return
}
// 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 {
print("Warning: \(message)")
return
}
// 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.main(defaultVerb: helpCommand.verb) { error in
if let sourceError = error.sourceError {
var stderr = StderrOutputStream()
print(sourceError.localizedDescription, to: &stderr)
}
exit(Int32(error.code.rawValue))
printError(String(describing: error))
exit(1)
}