OptionGroup.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Argument Parser open source project
  4. //
  5. // Copyright (c) 2020 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. /// A wrapper that transparently includes a parsable type.
  12. ///
  13. /// Use an option group to include a group of options, flags, or arguments
  14. /// declared in a parsable type.
  15. ///
  16. /// ```swift
  17. /// struct GlobalOptions: ParsableArguments {
  18. /// @Flag(name: .shortAndLong)
  19. /// var verbose: Bool = false
  20. ///
  21. /// @Argument var values: [Int]
  22. /// }
  23. ///
  24. /// struct Options: ParsableArguments {
  25. /// @Option var name: String
  26. /// @OptionGroup var globals: GlobalOptions
  27. /// }
  28. /// ```
  29. ///
  30. /// The flag and positional arguments declared as part of `GlobalOptions` are
  31. /// included when parsing `Options`.
  32. @propertyWrapper
  33. public struct OptionGroup<Value: ParsableArguments>: Decodable, ParsedWrapper {
  34. internal var _parsedValue: Parsed<Value>
  35. internal var _visibility: ArgumentVisibility
  36. // FIXME: Adding this property works around the crasher described in
  37. // https://github.com/apple/swift-argument-parser/issues/338
  38. internal var _dummy: Bool = false
  39. /// The title to use in the help screen for this option group.
  40. public var title: String = ""
  41. internal init(_parsedValue: Parsed<Value>) {
  42. self._parsedValue = _parsedValue
  43. self._visibility = .default
  44. }
  45. public init(from _decoder: Decoder) throws {
  46. if let d = _decoder as? SingleValueDecoder,
  47. let value = try? d.previousValue(Value.self)
  48. {
  49. self.init(_parsedValue: .value(value))
  50. } else {
  51. try self.init(_decoder: _decoder)
  52. if let d = _decoder as? SingleValueDecoder {
  53. d.saveValue(wrappedValue)
  54. }
  55. }
  56. do {
  57. try wrappedValue.validate()
  58. } catch {
  59. throw ParserError.userValidationError(error)
  60. }
  61. }
  62. /// Creates a property that represents another parsable type, using the
  63. /// specified title and visibility.
  64. ///
  65. /// - Parameters:
  66. /// - title: A title for grouping this option group's members in your
  67. /// command's help screen. If `title` is empty, the members will be
  68. /// displayed alongside the other arguments, flags, and options declared
  69. /// by your command.
  70. /// - visibility: The visibility to use for the entire option group.
  71. public init(
  72. title: String = "",
  73. visibility: ArgumentVisibility = .default
  74. ) {
  75. self.init(
  76. _parsedValue: .init { parentKey in
  77. var args = ArgumentSet(
  78. Value.self, visibility: .private, parent: parentKey)
  79. if !title.isEmpty {
  80. args.content.withEach {
  81. $0.help.parentTitle = title
  82. }
  83. }
  84. args.content = args.content.map { arg in
  85. arg.reducingHelpVisibility(to: visibility)
  86. }
  87. return args
  88. })
  89. self._visibility = visibility
  90. self.title = title
  91. }
  92. /// The value presented by this property wrapper.
  93. public var wrappedValue: Value {
  94. get {
  95. switch _parsedValue {
  96. case .value(let v):
  97. return v
  98. case .definition:
  99. configurationFailure(directlyInitializedError)
  100. }
  101. }
  102. set {
  103. _parsedValue = .value(newValue)
  104. }
  105. }
  106. }
  107. extension OptionGroup: Sendable where Value: Sendable {}
  108. extension OptionGroup: CustomStringConvertible {
  109. public var description: String {
  110. switch _parsedValue {
  111. case .value(let v):
  112. return String(describing: v)
  113. case .definition:
  114. return "OptionGroup(*definition*)"
  115. }
  116. }
  117. }
  118. // Experimental use with caution
  119. extension OptionGroup {
  120. @available(*, deprecated, renamed: "init(visibility:)")
  121. public init(_hiddenFromHelp: Bool) {
  122. self.init(visibility: .hidden)
  123. }
  124. /// Creates a property that represents another parsable type.
  125. @available(*, deprecated, renamed: "init(visibility:)")
  126. @_disfavoredOverload
  127. public init() {
  128. self.init(visibility: .default)
  129. }
  130. }
  131. // MARK: Deprecated
  132. extension OptionGroup {
  133. @_disfavoredOverload
  134. @available(*, deprecated, renamed: "init(title:visibility:)")
  135. public init(
  136. visibility _visibility: ArgumentVisibility = .default
  137. ) {
  138. self.init(title: "", visibility: _visibility)
  139. }
  140. }