Skip to content

Don't error when decoding optional unparsed values #286

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 1 commit into from
Mar 7, 2021
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
13 changes: 13 additions & 0 deletions Sources/ArgumentParser/Parsing/ArgumentDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ final class ParsedArgumentsContainer<K>: KeyedDecodingContainerProtocol where K
return try type.init(from: subDecoder)
}

func decodeIfPresent<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T? where T : Decodable {
let subDecoder = SingleValueDecoder(userInfo: decoder.userInfo, underlying: decoder, codingPath: codingPath + [key], key: InputKey(key), parsedElement: element(forKey: key))
do {
return try type.init(from: subDecoder)
} catch let error as ParserError {
if case .noValue = error {
return nil
} else {
throw error
}
}
}

func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
fatalError()
}
Expand Down
42 changes: 0 additions & 42 deletions Tests/ArgumentParserEndToEndTests/SimpleEndToEndTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,45 +115,3 @@ extension SimpleEndToEndTests {
XCTAssertThrowsError(try Baz.parse(["--name", "--format", "Bar", "Foo"]))
}
}

// MARK: Two values + unparsed variable

fileprivate struct Qux: ParsableArguments {
@Option() var name: String
@Flag() var verbose = false
var count = 0
}

fileprivate struct Quizzo: ParsableArguments {
@Option() var name: String
@Flag() var verbose = false
let count = 0
}

extension SimpleEndToEndTests {
func testParsing_TwoPlusUnparsed() throws {
AssertParse(Qux.self, ["--name", "Qux"]) { qux in
XCTAssertEqual(qux.name, "Qux")
XCTAssertFalse(qux.verbose)
XCTAssertEqual(qux.count, 0)
}
AssertParse(Qux.self, ["--name", "Qux", "--verbose"]) { qux in
XCTAssertEqual(qux.name, "Qux")
XCTAssertTrue(qux.verbose)
XCTAssertEqual(qux.count, 0)
}

AssertParse(Quizzo.self, ["--name", "Qux", "--verbose"]) { quizzo in
XCTAssertEqual(quizzo.name, "Qux")
XCTAssertTrue(quizzo.verbose)
XCTAssertEqual(quizzo.count, 0)
}
}

func testParsing_TwoPlusUnparsed_Fails() throws {
XCTAssertThrowsError(try Qux.parse([]))
XCTAssertThrowsError(try Qux.parse(["--name"]))
XCTAssertThrowsError(try Qux.parse(["--name", "Qux", "--count"]))
XCTAssertThrowsError(try Qux.parse(["--name", "Qux", "--count", "2"]))
}
}
108 changes: 108 additions & 0 deletions Tests/ArgumentParserEndToEndTests/UnparsedValuesEndToEndTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

import XCTest
import ArgumentParserTestHelpers
import ArgumentParser

final class UnparsedValuesEndToEndTests: XCTestCase {}

// MARK: Two values + unparsed variable

fileprivate struct Qux: ParsableArguments {
@Option() var name: String
@Flag() var verbose = false
var count = 0
}

fileprivate struct Quizzo: ParsableArguments {
@Option() var name: String
@Flag() var verbose = false
let count = 0
}

extension UnparsedValuesEndToEndTests {
func testParsing_TwoPlusUnparsed() throws {
AssertParse(Qux.self, ["--name", "Qux"]) { qux in
XCTAssertEqual(qux.name, "Qux")
XCTAssertFalse(qux.verbose)
XCTAssertEqual(qux.count, 0)
}
AssertParse(Qux.self, ["--name", "Qux", "--verbose"]) { qux in
XCTAssertEqual(qux.name, "Qux")
XCTAssertTrue(qux.verbose)
XCTAssertEqual(qux.count, 0)
}

AssertParse(Quizzo.self, ["--name", "Qux", "--verbose"]) { quizzo in
XCTAssertEqual(quizzo.name, "Qux")
XCTAssertTrue(quizzo.verbose)
XCTAssertEqual(quizzo.count, 0)
}
}

func testParsing_TwoPlusUnparsed_Fails() throws {
XCTAssertThrowsError(try Qux.parse([]))
XCTAssertThrowsError(try Qux.parse(["--name"]))
XCTAssertThrowsError(try Qux.parse(["--name", "Qux", "--count"]))
XCTAssertThrowsError(try Qux.parse(["--name", "Qux", "--count", "2"]))
}
}

// MARK: Nested unparsed decodable type


fileprivate struct Foo: ParsableCommand {
@Flag var foo: Bool = false
var config: Config?
@OptionGroup var opt: OptionalArguments
@OptionGroup var def: DefaultedArguments
}

fileprivate struct Config: Decodable {
var name: String
var age: Int
}

fileprivate struct OptionalArguments: ParsableArguments {
@Argument var title: String?
@Option var edition: Int?
}

fileprivate struct DefaultedArguments: ParsableArguments {
@Option var one = 1
@Option var two = 2
}

extension UnparsedValuesEndToEndTests {
func testUnparsedNestedValues() {
AssertParse(Foo.self, []) { foo in
XCTAssertFalse(foo.foo)
XCTAssertNil(foo.opt.title)
XCTAssertNil(foo.opt.edition)
XCTAssertEqual(1, foo.def.one)
XCTAssertEqual(2, foo.def.two)
}

AssertParse(Foo.self, ["--foo", "--edition", "5", "Hello", "--one", "2", "--two", "1"]) { foo in
XCTAssertTrue(foo.foo)
XCTAssertEqual("Hello", foo.opt.title)
XCTAssertEqual(5, foo.opt.edition)
XCTAssertEqual(2, foo.def.one)
XCTAssertEqual(1, foo.def.two)
}
}

func testUnparsedNestedValues_Fails() {
XCTAssertThrowsError(try Foo.parse(["--edition", "aaa"]))
XCTAssertThrowsError(try Foo.parse(["--one", "aaa"]))
}
}