1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
@_implementationOnly import protocol Foundation.LocalizedError
@_implementationOnly import class Foundation.NSError
enum MessageInfo {
case help(text: String)
case validation(message: String, usage: String, help: String)
case other(message: String, exitCode: ExitCode)
init(error: Error, type: ParsableArguments.Type) {
var commandStack: [ParsableCommand.Type]
var parserError: ParserError? = nil
switch error {
case let e as CommandError:
commandStack = e.commandStack
parserError = e.parserError
// Exit early on built-in requests
switch e.parserError {
case .helpRequested(let visibility):
self = .help(text: HelpGenerator(commandStack: e.commandStack, visibility: visibility).rendered())
return
case .dumpHelpRequested:
self = .help(text: DumpHelpGenerator(commandStack: e.commandStack).rendered())
return
case .versionRequested:
let versionString = commandStack
.map { $0.configuration.version }
.last(where: { !$0.isEmpty })
?? "Unspecified version"
self = .help(text: versionString)
return
case .completionScriptRequested(let shell):
do {
let completionsGenerator = try CompletionsGenerator(command: type.asCommand, shellName: shell)
self = .help(text: completionsGenerator.generateCompletionScript())
return
} catch {
self.init(error: error, type: type)
return
}
case .completionScriptCustomResponse(let output):
self = .help(text: output)
return
default:
break
}
case let e as ParserError:
// Send ParserErrors back through the CommandError path
self.init(error: CommandError(commandStack: [type.asCommand], parserError: e), type: type)
return
default:
commandStack = [type.asCommand]
// if the error wasn't one of our two Error types, wrap it as a userValidationError
// to be handled appropriately below
parserError = .userValidationError(error)
}
var usage = HelpGenerator(commandStack: commandStack, visibility: .default).usageMessage()
let commandNames = commandStack.map { $0._commandName }.joined(separator: " ")
if let helpName = commandStack.getPrimaryHelpName() {
if !usage.isEmpty {
usage += "\n"
}
usage += " See '\(commandNames) \(helpName.synopsisString)' for more information."
}
// Parsing errors and user-thrown validation errors have the usage
// string attached. Other errors just get the error message.
if case .userValidationError(let error) = parserError {
switch error {
case let error as ValidationError:
self = .validation(message: error.message, usage: usage, help: "")
case let error as CleanExit:
switch error.base {
case .helpRequest(let command):
if let command = command {
commandStack = CommandParser(type.asCommand).commandStack(for: command)
}
self = .help(text: HelpGenerator(commandStack: commandStack, visibility: .default).rendered())
case .dumpRequest(let command):
if let command = command {
commandStack = CommandParser(type.asCommand).commandStack(for: command)
}
self = .help(text: DumpHelpGenerator(commandStack: commandStack).rendered())
case .message(let message):
self = .help(text: message)
}
case let exitCode as ExitCode:
self = .other(message: "", exitCode: exitCode)
case let error as LocalizedError where error.errorDescription != nil:
self = .other(message: error.errorDescription!, exitCode: .failure)
default:
if Swift.type(of: error) is NSError.Type {
self = .other(message: error.localizedDescription, exitCode: .failure)
} else {
self = .other(message: String(describing: error), exitCode: .failure)
}
}
} else if let parserError = parserError {
let usage: String = {
guard case ParserError.noArguments = parserError else { return usage }
return "\n" + HelpGenerator(commandStack: [type.asCommand], visibility: .default).rendered()
}()
let argumentSet = ArgumentSet(commandStack.last!, visibility: .default, parent: nil)
let message = argumentSet.errorDescription(error: parserError) ?? ""
let helpAbstract = argumentSet.helpDescription(error: parserError) ?? ""
self = .validation(message: message, usage: usage, help: helpAbstract)
} else {
self = .other(message: String(describing: error), exitCode: .failure)
}
}
var message: String {
switch self {
case .help(text: let text):
return text
case .validation(message: let message, usage: _, help: _):
return message
case .other(let message, _):
return message
}
}
func fullText(for args: ParsableArguments.Type) -> String {
switch self {
case .help(text: let text):
return text
case .validation(message: let message, usage: let usage, help: let help):
let helpMessage = help.isEmpty ? "" : "Help: \(help)\n"
let errorMessage = message.isEmpty ? "" : "\(args._errorLabel): \(message)\n"
return errorMessage + helpMessage + usage
case .other(let message, _):
return message.isEmpty ? "" : "\(args._errorLabel): \(message)"
}
}
var shouldExitCleanly: Bool {
switch self {
case .help: return true
case .validation, .other: return false
}
}
var exitCode: ExitCode {
switch self {
case .help: return ExitCode.success
case .validation: return ExitCode.validationFailure
case .other(_, let exitCode): return exitCode
}
}
}
|