File: MessageInfo.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (172 lines) | stat: -rw-r--r-- 6,288 bytes parent folder | download
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
    }
  }
}