Skip to content
This repository has been archived by the owner on Sep 6, 2018. It is now read-only.

Error for wrong type placeholders #44

Merged
merged 3 commits into from
May 28, 2017
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ Due to the removal of legacy code, there are a few breaking changes in this new

### New Features

_None_
* Throw an error if a format string has mismatching types for the same placeholde position.
[David Jennes](https://github.com/djbe)
[#44](https://github.com/SwiftGen/SwiftGenKit/issues/44)

### Internal Changes

Expand Down
19 changes: 13 additions & 6 deletions Sources/Parsers/StringsFileParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import PathKit
public enum StringsFileParserError: Error, CustomStringConvertible {
case failureOnLoading(path: String)
case invalidFormat
case invalidPlaceholder(previous: StringsFileParser.PlaceholderType, new: StringsFileParser.PlaceholderType)

public var description: String {
switch self {
case .failureOnLoading(let path):
return "Failed to load a file at \"\(path)\""
case .invalidFormat:
return "Invalid strings file"
case .invalidPlaceholder(let previous, let new):
return "Invalid placeholder type \(new) (previous: \(previous))"
}
}
}
Expand Down Expand Up @@ -44,7 +47,7 @@ public final class StringsFileParser {
}

for (key, translation) in dict {
addEntry(Entry(key: key, translation: translation))
addEntry(try Entry(key: key, translation: translation))
}
}

Expand Down Expand Up @@ -81,8 +84,8 @@ public final class StringsFileParser {
}
}

public static func placeholders(fromFormat str: String) -> [PlaceholderType] {
return StringsFileParser.placeholders(fromFormat: str)
public static func placeholders(fromFormat str: String) throws -> [PlaceholderType] {
return try StringsFileParser.placeholders(fromFormat: str)
}
}

Expand All @@ -104,8 +107,8 @@ public final class StringsFileParser {
self.init(key: key, translation: translation, types: types)
}

public init(key: String, translation: String) {
let types = PlaceholderType.placeholders(fromFormat: translation)
public init(key: String, translation: String) throws {
let types = try PlaceholderType.placeholders(fromFormat: translation)
self.init(key: key, translation: translation, types: types)
}
}
Expand Down Expand Up @@ -133,7 +136,7 @@ public final class StringsFileParser {
}()

// "I give %d apples to %@" --> [.Int, .String]
private static func placeholders(fromFormat formatString: String) -> [PlaceholderType] {
private static func placeholders(fromFormat formatString: String) throws -> [PlaceholderType] {
let range = NSRange(location: 0, length: (formatString as NSString).length)

// Extract the list of chars (conversion specifiers) and their optional positional specifier
Expand Down Expand Up @@ -178,6 +181,10 @@ public final class StringsFileParser {
while list.count <= insertionPos-1 {
list.append(.unknown)
}
let previous = list[insertionPos-1]
guard previous == .unknown || previous == p else {
throw StringsFileParserError.invalidPlaceholder(previous: previous, new: p)
}
list[insertionPos-1] = p
}
}
Expand Down
56 changes: 36 additions & 20 deletions Tests/SwiftGenKitTests/StringParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,72 @@ import XCTest
import SwiftGenKit

class StringParserTests: XCTestCase {
func testParseStringPlaceholder() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%@")
func testParseStringPlaceholder() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%@")
XCTAssertEqual(placeholders, [.object])
}

func testParseFloatPlaceholder() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%f")
func testParseFloatPlaceholder() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%f")
XCTAssertEqual(placeholders, [.float])
}

func testParseDoublePlaceholders() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%g-%e")
func testParseDoublePlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%g-%e")
XCTAssertEqual(placeholders, [.float, .float])
}

func testParseFloatWithPrecisionPlaceholders() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%1.2f : %.3f : %+3f : %-6.2f")
func testParseFloatWithPrecisionPlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%1.2f : %.3f : %+3f : %-6.2f")
XCTAssertEqual(placeholders, [.float, .float, .float, .float])
}

func testParseIntPlaceholders() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%d-%i-%o-%u-%x")
func testParseIntPlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%d-%i-%o-%u-%x")
XCTAssertEqual(placeholders, [.int, .int, .int, .int, .int])
}

func testParseCCharAndStringPlaceholders() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%c-%s")
func testParseCCharAndStringPlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%c-%s")
XCTAssertEqual(placeholders, [.char, .cString])
}

func testParsePositionalPlaceholders() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%2$d-%4$f-%3$@-%c")
func testParsePositionalPlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%2$d-%4$f-%3$@-%c")
XCTAssertEqual(placeholders, [.char, .int, .object, .float])
}

func testParseComplexFormatPlaceholders() {
func testParseComplexFormatPlaceholders() throws {
let format = "%2$1.3d - %4$-.7f - %3$@ - %% - %5$+3c - %%"
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: format)
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: format)
// positions 2, 4, 3, 5 set to Int, Float, Object, Char, and position 1 not matched, defaulting to Unknown
XCTAssertEqual(placeholders, [.unknown, .int, .object, .float, .char])
}

func testParseEvenEscapePercentSign() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%%foo")
func testParseDuplicateFormatPlaceholders() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "Text: %1$@; %1$@.")
XCTAssertEqual(placeholders, [.object])
}

func testParseErrorOnTypeMismatch() throws {
do {
_ = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "Text: %1$@; %1$ld.")
XCTFail("Code did parse string successfully while it was expected to fail for bad syntax")
} catch StringsFileParserError.invalidPlaceholder {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful

// That's the expected exception we want to happen
} catch let error {
XCTFail("Unexpected error occured while parsing: \(error)")
}
}

func testParseEvenEscapePercentSign() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%%foo")
// Must NOT map to [.float]
XCTAssertEqual(placeholders, [])
}

func testParseOddEscapePercentSign() {
let placeholders = StringsFileParser.PlaceholderType.placeholders(fromFormat: "%%%foo")
func testParseOddEscapePercentSign() throws {
let placeholders = try StringsFileParser.PlaceholderType.placeholders(fromFormat: "%%%foo")
// Should map to [.float]
XCTAssertEqual(placeholders, [.float])
}
Expand Down