Skip to content

Remove ExpressibleByArgument conformance from Optional #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions Sources/ArgumentParser/Parsable Properties/Argument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,9 @@ extension Argument: DecodableParsedWrapper where Value: Decodable {}
extension Argument where Value: ExpressibleByArgument {
/// Creates a property that reads its value from an argument.
///
/// If the property has an `Optional` type, the argument is optional and
/// defaults to `nil`.
///
/// - Parameters:
/// - initial: A default value to use for this property.
/// - initial: A default value to use for this property. If `initial` is
/// `nil`, the user must supply a value for this argument.
/// - help: Information about how to use this argument.
public init(
default initial: Value? = nil,
Expand Down Expand Up @@ -130,6 +128,48 @@ public enum ArgumentArrayParsingStrategy {
}

extension Argument {
/// Creates an optional property that reads its value from an argument.
///
/// The argument is optional for the caller of the command and defaults to
/// `nil`.
///
/// - Parameter help: Information about how to use this argument.
public init<T: ExpressibleByArgument>(
help: ArgumentHelp? = nil
) where Value == T? {
self.init(_parsedValue: .init { key in
var arg = ArgumentDefinition(
key: key,
kind: .positional,
parsingStrategy: .nextAsValue,
parser: T.init(argument:),
default: nil)
arg.help.help = help
return ArgumentSet(arg.optional)
})
}

@available(*, deprecated, message: """
Default values don't make sense for optional properties.
Remove the 'default' parameter if its value is nil,
or make your property non-optional if it's non-nil.
""")
public init<T: ExpressibleByArgument>(
default initial: T?,
help: ArgumentHelp? = nil
) where Value == T? {
self.init(_parsedValue: .init { key in
ArgumentSet(
key: key,
kind: .positional,
parsingStrategy: .nextAsValue,
parseType: T.self,
name: .long,
default: initial,
help: help)
})
}

/// Creates a property that reads its value from an argument, parsing with
/// the given closure.
///
Expand Down
59 changes: 53 additions & 6 deletions Sources/ArgumentParser/Parsable Properties/Option.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,12 @@ extension Option: DecodableParsedWrapper where Value: Decodable {}
extension Option where Value: ExpressibleByArgument {
/// Creates a property that reads its value from a labeled option.
///
/// If the property has an `Optional` type, or you provide a non-`nil`
/// value for the `initial` parameter, specifying this option is not
/// required.
///
/// - Parameters:
/// - name: A specification for what names are allowed for this flag.
/// - initial: A default value to use for this property. If `initial` is
/// `nil`, this option and value are required from the user.
/// - parsingStrategy: The behavior to use when looking for this option's
/// value.
/// - help: Information about how to use this option.
public init(
name: NameSpecification = .long,
Expand Down Expand Up @@ -216,17 +214,66 @@ public enum ArrayParsingStrategy {
}

extension Option {
/// Creates a property that reads its value from a labeled option, parsing
/// with the given closure.
/// Creates a property that reads its value from a labeled option.
///
/// If the property has an `Optional` type, or you provide a non-`nil`
/// value for the `initial` parameter, specifying this option is not
/// required.
///
/// - Parameters:
/// - name: A specification for what names are allowed for this flag.
/// - parsingStrategy: The behavior to use when looking for this option's
/// value.
/// - help: Information about how to use this option.
public init<T: ExpressibleByArgument>(
name: NameSpecification = .long,
parsing parsingStrategy: SingleValueParsingStrategy = .next,
help: ArgumentHelp? = nil
) where Value == T? {
self.init(_parsedValue: .init { key in
var arg = ArgumentDefinition(
key: key,
kind: .name(key: key, specification: name),
parsingStrategy: ArgumentDefinition.ParsingStrategy(parsingStrategy),
parser: T.init(argument:),
default: nil)
arg.help.help = help
return ArgumentSet(arg.optional)
})
}

@available(*, deprecated, message: """
Default values don't make sense for optional properties.
Remove the 'default' parameter if its value is nil,
or make your property non-optional if it's non-nil.
""")
public init<T: ExpressibleByArgument>(
name: NameSpecification = .long,
default initial: T?,
parsing parsingStrategy: SingleValueParsingStrategy = .next,
help: ArgumentHelp? = nil
) where Value == T? {
self.init(_parsedValue: .init { key in
var arg = ArgumentDefinition(
key: key,
kind: .name(key: key, specification: name),
parsingStrategy: ArgumentDefinition.ParsingStrategy(parsingStrategy),
parser: T.init(argument:),
default: initial)
arg.help.help = help
return ArgumentSet(arg.optional)
})
}

/// Creates a property that reads its value from a labeled option, parsing
/// with the given closure.
///
/// - Parameters:
/// - name: A specification for what names are allowed for this flag.
/// - initial: A default value to use for this property. If `initial` is
/// `nil`, this option and value are required from the user.
/// - parsingStrategy: The behavior to use when looking for this option's
/// value.
/// - help: Information about how to use this option.
/// - transform: A closure that converts a string into this property's
/// type or throws an error.
Expand Down
17 changes: 0 additions & 17 deletions Sources/ArgumentParser/Parsable Types/ExpressibleByArgument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,6 @@ extension String: ExpressibleByArgument {
}
}

extension Optional: ExpressibleByArgument where Wrapped: ExpressibleByArgument {
public init?(argument: String) {
if let value = Wrapped(argument: argument) {
self = value
} else {
return nil
}
}

public var defaultValueDescription: String {
guard let value = self else {
return "none"
}
return "\(value)"
}
}

extension RawRepresentable where Self: ExpressibleByArgument, RawValue: ExpressibleByArgument {
public init?(argument: String) {
if let value = RawValue(argument: argument) {
Expand Down
1 change: 1 addition & 0 deletions Sources/ArgumentParser/Parsing/ArgumentDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ extension ArgumentDefinition: CustomDebugStringConvertible {
extension ArgumentDefinition {
var optional: ArgumentDefinition {
var result = self

result.help.options.insert(.isOptional)
return result
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ArgumentParser/Parsing/ArgumentSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ extension ArgumentSet {

extension ArgumentDefinition {
/// Create a unary / argument that parses using the given closure.
fileprivate init<A>(key: InputKey, kind: ArgumentDefinition.Kind, parsingStrategy: ParsingStrategy = .nextAsValue, parser: @escaping (String) -> A?, parseType type: A.Type = A.self, default initial: A?) {
init<A>(key: InputKey, kind: ArgumentDefinition.Kind, parsingStrategy: ParsingStrategy = .nextAsValue, parser: @escaping (String) -> A?, parseType type: A.Type = A.self, default initial: A?) {
let initialValueCreator: (InputOrigin, inout ParsedValues) throws -> Void
if let initialValue = initial {
initialValueCreator = { origin, values in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,10 @@ fileprivate struct AlmostAllArguments: ParsableArguments {
@Argument(default: 0) var c2: Int?

@Argument(default: 0, help: "", transform: { _ in 0 }) var d: Int?
@Argument(default: 0) var d1: Int?
@Argument(help: "") var d2: Int?
@Argument(transform: { _ in 0 }) var d3: Int?
@Argument(help: "", transform: { _ in 0 }) var d4: Int?
@Argument(default: 0, transform: { _ in 0 }) var d5: Int?
@Argument(default: 0, help: "") var d6: Int?

@Argument(parsing: .remaining, help: "") var e: [Int]
@Argument() var e0: [Int]
Expand Down