| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- //===----------------------------------------------------------------------===//
- //
- // 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
- //
- //===----------------------------------------------------------------------===//
- /// A specification for how to represent a property as a command-line argument
- /// label.
- public struct NameSpecification: ExpressibleByArrayLiteral {
- /// An individual property name translation.
- public struct Element: Hashable, Sendable {
- internal enum Representation: Hashable {
- case long
- case customLong(_ name: String, withSingleDash: Bool)
- case short
- case customShort(_ char: Character, allowingJoined: Bool)
- }
- internal var base: Representation
- /// Use the property's name, converted to lowercase with words separated by
- /// hyphens.
- ///
- /// For example, a property named `allowLongNames` would be converted to the
- /// label `--allow-long-names`.
- public static var long: Element {
- self.init(base: .long)
- }
- /// Use the given string instead of the property's name.
- ///
- /// To create a single-dash argument, pass `true` as `withSingleDash`. Note
- /// that combining single-dash options and options with short,
- /// single-character names can lead to ambiguities for the user.
- ///
- /// - Parameters:
- /// - name: The name of the option or flag.
- /// - withSingleDash: A Boolean value indicating whether to use a single
- /// dash as the prefix. If `false`, the name has a double-dash prefix.
- ///
- /// - Returns: A `long` name specification with the requested `name`.
- public static func customLong(
- _ name: String,
- withSingleDash: Bool = false
- ) -> Element {
- self.init(base: .customLong(name, withSingleDash: withSingleDash))
- }
- /// Use the first character of the property's name as a short option label.
- ///
- /// For example, a property named `verbose` would be converted to the
- /// label `-v`. Short labels can be combined into groups.
- public static var short: Element {
- self.init(base: .short)
- }
- /// Use the given character as a short option label.
- ///
- /// When passing `true` as `allowingJoined` in an `@Option` declaration,
- /// the user can join a value with the option name. For example, if an
- /// option is declared as `-D`, allowing joined values, a user could pass
- /// `-Ddebug` to specify `debug` as the value for that option.
- ///
- /// - Parameters:
- /// - char: The name of the option or flag.
- /// - allowingJoined: A Boolean value indicating whether this short name
- /// allows a joined value.
- ///
- /// - Returns: A `short` name specification with the requested `char`.
- public static func customShort(
- _ char: Character,
- allowingJoined: Bool = false
- ) -> Element {
- self.init(base: .customShort(char, allowingJoined: allowingJoined))
- }
- }
- var elements: [Element]
- public init<S>(_ sequence: S) where S: Sequence, Element == S.Element {
- self.elements = sequence.uniquing()
- }
- public init(arrayLiteral elements: Element...) {
- self.init(elements)
- }
- }
- extension NameSpecification: Sendable {}
- extension NameSpecification {
- /// Use the property's name converted to lowercase with words separated by
- /// hyphens.
- ///
- /// For example, a property named `allowLongNames` would be converted to the
- /// label `--allow-long-names`.
- public static var long: NameSpecification { [.long] }
- /// Use the given string instead of the property's name.
- ///
- /// To create a single-dash argument, pass `true` as `withSingleDash`. Note
- /// that combining single-dash options and options with short,
- /// single-character names can lead to ambiguities for the user.
- ///
- /// - Parameters:
- /// - name: The name of the option or flag.
- /// - withSingleDash: A Boolean value indicating whether to use a single
- /// dash as the prefix. If `false`, the name has a double-dash prefix.
- ///
- /// - Returns: A `long` name specification with the requested `name`.
- public static func customLong(
- _ name: String,
- withSingleDash: Bool = false
- ) -> NameSpecification {
- [.customLong(name, withSingleDash: withSingleDash)]
- }
- /// Use the first character of the property's name as a short option label.
- ///
- /// For example, a property named `verbose` would be converted to the
- /// label `-v`. Short labels can be combined into groups.
- public static var short: NameSpecification { [.short] }
- /// Use the given character as a short option label.
- ///
- /// When passing `true` as `allowingJoined` in an `@Option` declaration,
- /// the user can join a value with the option name. For example, if an
- /// option is declared as `-D`, allowing joined values, a user could pass
- /// `-Ddebug` to specify `debug` as the value for that option.
- ///
- /// - Parameters:
- /// - char: The name of the option or flag.
- /// - allowingJoined: A Boolean value indicating whether this short name
- /// allows a joined value.
- ///
- /// - Returns: A `short` name specification with the requested `char`.
- public static func customShort(
- _ char: Character,
- allowingJoined: Bool = false
- ) -> NameSpecification {
- [.customShort(char, allowingJoined: allowingJoined)]
- }
- /// Combine the `.short` and `.long` specifications to allow both long
- /// and short labels.
- ///
- /// For example, a property named `verbose` would be converted to both the
- /// long `--verbose` and short `-v` labels.
- public static var shortAndLong: NameSpecification { [.long, .short] }
- }
- extension NameSpecification.Element {
- /// Creates the argument name for this specification element.
- internal func name(for key: InputKey) -> Name? {
- switch self.base {
- case .long:
- return .long(key.name.convertedToSnakeCase(separator: "-"))
- case .short:
- guard let c = key.name.first else {
- fatalError(
- "Key '\(key.name)' has not characters to form short option name.")
- }
- return .short(c)
- case .customLong(let name, let withSingleDash):
- return withSingleDash
- ? .longWithSingleDash(name)
- : .long(name)
- case .customShort(let name, let allowingJoined):
- return .short(name, allowingJoined: allowingJoined)
- }
- }
- }
- extension NameSpecification {
- /// Creates the argument names for each element in the name specification.
- internal func makeNames(_ key: InputKey) -> [Name] {
- elements.compactMap { $0.name(for: key) }
- }
- }
- extension FlagInversion {
- /// Creates the enable and disable name(s) for the given flag.
- internal func enableDisableNamePair(
- for key: InputKey, name: NameSpecification
- ) -> ([Name], [Name]) {
- func makeNames(withPrefix prefix: String, includingShort: Bool) -> [Name] {
- name.elements.compactMap { element -> Name? in
- switch element.base {
- case .short, .customShort:
- return includingShort ? element.name(for: key) : nil
- case .long:
- let modifiedKey = InputKey(
- name: key.name.addingIntercappedPrefix(prefix), parent: key)
- return element.name(for: modifiedKey)
- case .customLong(let name, let withSingleDash):
- let modifiedName = name.addingPrefixWithAutodetectedStyle(prefix)
- let modifiedElement = NameSpecification.Element.customLong(
- modifiedName, withSingleDash: withSingleDash)
- return modifiedElement.name(for: key)
- }
- }
- }
- switch self.base {
- case .prefixedNo:
- return (
- name.makeNames(key),
- makeNames(withPrefix: "no", includingShort: false)
- )
- case .prefixedEnableDisable:
- return (
- makeNames(withPrefix: "enable", includingShort: true),
- makeNames(withPrefix: "disable", includingShort: false)
- )
- }
- }
- }
|