Skip to content

Commit

Permalink
Provides initial initializer parameter for Argument and Option
Browse files Browse the repository at this point in the history
…property wrappers

This allows using `Optional` initial values in addition to non-`Optional`, e.g. environment variables.
  • Loading branch information
tarbaiev-smg committed Feb 27, 2023
1 parent c5050aa commit ff43a17
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 12 deletions.
12 changes: 8 additions & 4 deletions Sources/ArgumentParser/Parsable Properties/Argument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,11 @@ extension Argument where Value: ExpressibleByArgument {
/// - Parameters:
/// - help: Information about how to use this argument.
/// - completion: Kind of completion provided to the user for this option.
/// - initial: An `Optional` initial value.
public init(
help: ArgumentHelp? = nil,
completion: CompletionKind? = nil
completion: CompletionKind? = nil,
initial: Value? = nil
) {
self.init(_parsedValue: .init { key in
let arg = ArgumentDefinition(
Expand All @@ -346,7 +348,7 @@ extension Argument where Value: ExpressibleByArgument {
kind: .positional,
help: help,
parsingStrategy: .default,
initial: nil,
initial: initial,
completion: completion)

return ArgumentSet(arg)
Expand Down Expand Up @@ -407,10 +409,12 @@ extension Argument {
/// - completion: Kind of completion provided to the user for this option.
/// - transform: A closure that converts a string into this property's
/// element type or throws an error.
/// - initial: An `Optional` initial value.
public init(
help: ArgumentHelp? = nil,
completion: CompletionKind? = nil,
transform: @escaping (String) throws -> Value
transform: @escaping (String) throws -> Value,
initial: Value? = nil
) {
self.init(_parsedValue: .init { key in
let arg = ArgumentDefinition(
Expand All @@ -420,7 +424,7 @@ extension Argument {
help: help,
parsingStrategy: .default,
transform: transform,
initial: nil,
initial: initial,
completion: completion)

return ArgumentSet(arg)
Expand Down
13 changes: 9 additions & 4 deletions Sources/ArgumentParser/Parsable Properties/Option.swift
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,13 @@ extension Option where Value: ExpressibleByArgument {
/// - parsingStrategy: The behavior to use when looking for this option's value.
/// - help: Information about how to use this option.
/// - completion: Kind of completion provided to the user for this option.
/// - initial: An `Optional` initial value.
public init(
name: NameSpecification = .long,
parsing parsingStrategy: SingleValueParsingStrategy = .next,
help: ArgumentHelp? = nil,
completion: CompletionKind? = nil
completion: CompletionKind? = nil,
initial: Value? = nil
) {
self.init(_parsedValue: .init { key in
let arg = ArgumentDefinition(
Expand All @@ -312,7 +314,7 @@ extension Option where Value: ExpressibleByArgument {
kind: .name(key: key, specification: name),
help: help,
parsingStrategy: parsingStrategy.base,
initial: nil,
initial: initial,
completion: completion)

return ArgumentSet(arg)
Expand Down Expand Up @@ -373,12 +375,15 @@ extension Option {
/// - parsingStrategy: The behavior to use when looking for this option's value.
/// - help: Information about how to use this option.
/// - completion: Kind of completion provided to the user for this option.
/// - transform: A closure that converts a string into this property's type
/// - initial: An `Optional` initial value.
public init(
name: NameSpecification = .long,
parsing parsingStrategy: SingleValueParsingStrategy = .next,
help: ArgumentHelp? = nil,
completion: CompletionKind? = nil,
transform: @escaping (String) throws -> Value
transform: @escaping (String) throws -> Value,
initial: Value? = nil
) {
self.init(_parsedValue: .init { key in
let arg = ArgumentDefinition(
Expand All @@ -388,7 +393,7 @@ extension Option {
help: help,
parsingStrategy: parsingStrategy.base,
transform: transform,
initial: nil,
initial: initial,
completion: completion)

return ArgumentSet(arg)
Expand Down
70 changes: 66 additions & 4 deletions Tests/ArgumentParserEndToEndTests/DefaultsEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,17 @@ fileprivate struct OptionPropertyInitArguments_Default: ParsableArguments {
var transformedData: String = "test"
}

fileprivate struct OptionPropertyInitArguments_NoDefault_NoTransform: ParsableArguments {
fileprivate struct RequiredOptionPropertyInitArguments_Initial: ParsableArguments {
@Option(initial: "test")
var data: String
}

fileprivate struct RequiredOptionPropertyInitArguments_Transform_Initial: ParsableArguments {
@Option(transform: exclaim, initial: "test")
var data: String
}

fileprivate struct RequiredOptionPropertyInitArguments_NoDefault_NoTransform: ParsableArguments {
@Option()
var data: String
}
Expand All @@ -412,12 +422,37 @@ extension DefaultsEndToEndTests {

/// Tests that *not* providing a default value still parses the argument correctly from the command-line.
/// This test is almost certainly duplicated by others in the repository, but allows for quick use of test filtering during development on the initialization functionality.
func testParsing_OptionPropertyInit_NoDefault_NoTransform() throws {
AssertParse(OptionPropertyInitArguments_NoDefault_NoTransform.self, ["--data", "test"]) { arguments in
func testParsing_RequiredOptionPropertyInit_NoDefault_NoTransform() throws {
AssertParse(RequiredOptionPropertyInitArguments_NoDefault_NoTransform.self, ["--data", "test"]) { arguments in
XCTAssertEqual(arguments.data, "test")
}
}

func testParsing_RequiredOptionPropertyInit_NoDefault_NoTransform_NoInput_Fails() throws {
XCTAssertThrowsError(try RequiredOptionPropertyInitArguments_NoDefault_NoTransform.parse([]))
XCTAssertThrowsError(try RequiredOptionPropertyInitArguments_NoDefault_NoTransform.parse(["--data"]))
}

func testParsing_RequiredOptionPropertyInitArguments_Initial_UsesInitialValue() {
AssertParse(RequiredOptionPropertyInitArguments_Initial.self, []) { arguments in
XCTAssertEqual(arguments.data, "test")
}
}

func testParsing_RequiredOptionPropertyInitArguments_Initial_IncompleteInput_Fails() throws {
XCTAssertThrowsError(try RequiredOptionPropertyInitArguments_Initial.parse(["--data"]))
}

func testParsing_RequiredOptionPropertyInitArguments_Transform_Initial_UsesInitialValue() {
AssertParse(RequiredOptionPropertyInitArguments_Transform_Initial.self, []) { arguments in
XCTAssertEqual(arguments.data, "test")
}
}

func testParsing_RequiredOptionPropertyInitArguments_Transform_Initial_IncompleteInput_Fails() throws {
XCTAssertThrowsError(try RequiredOptionPropertyInitArguments_Transform_Initial.parse(["--data"]))
}

/// Tests that using default property initialization syntax on a property with a `transform` function provided parses the default value for the argument when nothing is provided from the command-line.
func testParsing_OptionPropertyInit_Default_Transform_UseDefault() throws {
AssertParse(OptionPropertyInitArguments_Default.self, []) { arguments in
Expand All @@ -439,6 +474,11 @@ extension DefaultsEndToEndTests {
XCTAssertEqual(arguments.transformedData, "test!")
}
}

func testParsing_OptionPropertyInit_NoDefault_Transform_NoInput_Fails() throws {
XCTAssertThrowsError(try OptionPropertyInitArguments_NoDefault_Transform.parse([]))
XCTAssertThrowsError(try OptionPropertyInitArguments_NoDefault_Transform.parse(["--transformed-data"]))
}
}


Expand All @@ -447,14 +487,24 @@ fileprivate struct ArgumentPropertyInitArguments_Default_NoTransform: ParsableAr
var data: String = "test"
}

fileprivate struct ArgumentPropertyInitArguments_Initial_NoTransform: ParsableArguments {
@Argument(initial: "test")
var data: String
}

fileprivate struct ArgumentPropertyInitArguments_NoDefault_NoTransform: ParsableArguments {
@Argument()
var data: String
}

fileprivate struct ArgumentPropertyInitArguments_Default_Transform: ParsableArguments {
@Argument(transform: exclaim)
var transformedData: String = "test"
var transformedData: String = "test"
}

fileprivate struct ArgumentPropertyInitArguments_Transform_Initial: ParsableArguments {
@Argument(transform: exclaim, initial: "test")
var transformedData: String
}

fileprivate struct ArgumentPropertyInitArguments_NoDefault_Transform: ParsableArguments {
Expand All @@ -470,6 +520,12 @@ extension DefaultsEndToEndTests {
}
}

func testParsing_ArgumentPropertyInit_Initial_NoTransform_UseDefault() throws {
AssertParse(ArgumentPropertyInitArguments_Initial_NoTransform.self, []) { arguments in
XCTAssertEqual(arguments.data, "test")
}
}

/// Tests that using default property initialization syntax parses the command-line-provided value for the argument when provided.
func testParsing_ArgumentPropertyInit_Default_NoTransform_OverrideDefault() throws {
AssertParse(ArgumentPropertyInitArguments_Default_NoTransform.self, ["test2"]) { arguments in
Expand All @@ -492,6 +548,12 @@ extension DefaultsEndToEndTests {
}
}

func testParsing_ArgumentPropertyInit_Transform_Initial_UseDefault() throws {
AssertParse(ArgumentPropertyInitArguments_Transform_Initial.self, []) { arguments in
XCTAssertEqual(arguments.transformedData, "test")
}
}

/// Tests that using default property initialization syntax on a property with a `transform` function provided parses and transforms the command-line-provided value for the argument when provided.
func testParsing_ArgumentPropertyInit_Default_Transform_OverrideDefault() throws {
AssertParse(ArgumentPropertyInitArguments_Default_Transform.self, ["test2"]) { arguments in
Expand Down

0 comments on commit ff43a17

Please sign in to comment.