Skip to content

Commit

Permalink
Merge pull request #48 from SwiftGen/feature/only-remove-multiple-spaces
Browse files Browse the repository at this point in the history
Filter: Only remove multiple spaces
  • Loading branch information
djbe authored Jun 4, 2017
2 parents 11f8e4c + 856b15d commit 4a73848
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 74 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Due to the removal of legacy code, there are a few breaking changes in this new
* Added the `removeNewlines` filter to remove newlines (and spaces) from a string.
[David Jennes](https://github.com/djbe)
[#47](https://github.com/SwiftGen/StencilSwiftKit/pull/47)
[#48](https://github.com/SwiftGen/StencilSwiftKit/pull/48)

### Internal Changes

Expand Down
38 changes: 21 additions & 17 deletions Documentation/filters-strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,27 @@ Transforms an arbitrary string so that only the first "word" is lowercased.

## Filter: `removeNewlines`

Removes all newlines and whitespace characters from the string.

| Input | Output |
|-----------------------|-----------------------|
| ` \ntest` | `test` |
| `test \n\t ` | `test` |
| `test\n test` | `testtest` |
| `\r\ntest\n test\n` | `testtest` |

By default it removes whitespace characters, unless a single optional argument is set to "false", "no" or "0":

| Input | Output |
|-----------------------|-----------------------|
| ` \ntest` | ` test` |
| `test \n\t ` | `test \t ` |
| `test\n test` | `test test` |
| `\r\ntest\n test\n` | `test test` |
This filter has a couple of modes that you can specifiy using an optional argument (defaults to "all"):

**all**: Removes all newlines and whitespace characters from the string.

| Input | Output |
|------------------------|-----------------------|
| ` \ntest` | `test` |
| `test \n\t ` | `test` |
| `test\n test` | `testtest` |
| `test, \ntest, \ntest` | `test,test,test` |
| ` test test ` | `testtest` |

**leading**: Removes leading whitespace characters from each line, and all newlines. Also trims the end result.

| Input | Output |
|------------------------|-----------------------|
| ` \ntest` | `test` |
| `test \n\t ` | `test` |
| `test\n test` | `testtest` |
| `test, \ntest, \ntest` | `test, test, test` |
| ` test test ` | `test test` |

## Filter: `snakeToCamelCase`

Expand Down
40 changes: 33 additions & 7 deletions Sources/Filters+Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import Foundation
import Stencil

enum RemoveNewlinesModes: String {
case all, leading
}

extension Filters {
enum Strings {
fileprivate static let reservedKeywords = [
Expand Down Expand Up @@ -71,7 +75,7 @@ extension Filters {
/// - Returns: the camel case string
/// - Throws: FilterError.invalidInputType if the value parameter isn't a string
static func snakeToCamelCase(_ value: Any?, arguments: [Any?]) throws -> Any? {
let stripLeading = try Filters.parseBool(from: arguments, index: 0, required: false) ?? false
let stripLeading = try Filters.parseBool(from: arguments, required: false) ?? false
guard let string = value as? String else { throw Filters.Error.invalidInputType }

let unprefixed: String
Expand Down Expand Up @@ -104,7 +108,7 @@ extension Filters {
/// - Returns: the snake case string
/// - Throws: FilterError.invalidInputType if the value parameter isn't a string
static func camelToSnakeCase(_ value: Any?, arguments: [Any?]) throws -> Any? {
let toLower = try Filters.parseBool(from: arguments, index: 0, required: false) ?? true
let toLower = try Filters.parseBool(from: arguments, required: false) ?? true
guard let string = value as? String else { throw Filters.Error.invalidInputType }

let snakeCase = try snakecase(string)
Expand All @@ -119,18 +123,40 @@ extension Filters {
return escapeReservedKeywords(in: string)
}

/// Removes newlines and other whitespace from a string. Takes an optional Mode argument:
/// - all (default): remove all newlines and whitespaces
/// - leading: remove newlines and only leading whitespaces
///
/// - Parameters:
/// - value: the value to be processed
/// - arguments: the arguments to the function; expecting zero or one mode argument
/// - Returns: the trimmed string
/// - Throws: FilterError.invalidInputType if the value parameter isn't a string
static func removeNewlines(_ value: Any?, arguments: [Any?]) throws -> Any? {
let removeSpaces = try Filters.parseBool(from: arguments, index: 0, required: false) ?? true
guard let string = value as? String else { throw Filters.Error.invalidInputType }
let mode = try Filters.parseEnum(from: arguments, default: RemoveNewlinesModes.all)

let set: CharacterSet = removeSpaces ? .whitespacesAndNewlines : .newlines
let result = string.components(separatedBy: set).joined()

return result
switch mode {
case .all:
return string
.components(separatedBy: .whitespacesAndNewlines)
.joined()
case .leading:
return string
.components(separatedBy: .newlines)
.map(removeLeadingWhitespaces(from:))
.joined()
.trimmingCharacters(in: .whitespaces)
}
}

// MARK: - Private methods

private static func removeLeadingWhitespaces(from string: String) -> String {
let chars = string.unicodeScalars.drop { CharacterSet.whitespaces.contains($0) }
return String(chars)
}

/// This returns the string with its first parameter uppercased.
/// - note: This is quite similar to `capitalise` except that this filter doesn't
/// lowercase the rest of the string but keeps it untouched.
Expand Down
23 changes: 22 additions & 1 deletion Sources/Filters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Stencil
enum Filters {
enum Error: Swift.Error {
case invalidInputType
case invalidOption(option: String)
}

/// Parses filter arguments for a boolean value, where true can by any one of: "true", "yes", "1", and
Expand All @@ -22,7 +23,7 @@ enum Filters {
/// - required: If true, the argument is required and function throws if missing.
/// If false, returns nil on missing args.
/// - Throws: Filters.Error.invalidInputType
static func parseBool(from arguments: [Any?], index: Int, required: Bool = true) throws -> Bool? {
static func parseBool(from arguments: [Any?], at index: Int = 0, required: Bool = true) throws -> Bool? {
guard index < arguments.count, let boolArg = arguments[index] as? String else {
if required {
throw Error.invalidInputType
Expand All @@ -40,4 +41,24 @@ enum Filters {
throw Error.invalidInputType
}
}

/// Parses filter arguments for an enum value (with a String rawvalue).
///
/// - Parameters:
/// - arguments: an array of argument values, may be empty
/// - index: the index in the arguments array
/// - default: The default value should no argument be provided
/// - Throws: Filters.Error.invalidInputType
static func parseEnum<T>(from arguments: [Any?], at index: Int = 0, default: T) throws -> T
where T: RawRepresentable, T.RawValue == String {

guard index < arguments.count else { return `default` }
let arg = arguments[index].map(String.init(describing:)) ?? `default`.rawValue

guard let result = T(rawValue: arg) else {
throw Filters.Error.invalidOption(option: arg)
}

return result
}
}
4 changes: 4 additions & 0 deletions StencilSwiftKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
82EF0CC0752D216C67279A16 /* Pods_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BF798509C76E5A9ACE03491 /* Pods_Tests.framework */; };
B5A3F2ED5DA57C06EF62BB82 /* ParseBoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A3FFC01B2145C4BFD8316A /* ParseBoolTests.swift */; };
DD0B6D5F1EDF7C2100C8862C /* ParseEnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0B6D5E1EDF7C2100C8862C /* ParseEnumTests.swift */; };
DD4393FF1E2D3EEB0047A332 /* MapNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4393FE1E2D3EEB0047A332 /* MapNodeTests.swift */; };
DD5F341B1E21993A00AEB5DA /* TestsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5F341A1E21993A00AEB5DA /* TestsHelper.swift */; };
DD5F342E1E21A3A200AEB5DA /* CallNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5F342A1E21A3A200AEB5DA /* CallNodeTests.swift */; };
Expand Down Expand Up @@ -59,6 +60,7 @@
4B3D39DBCD15D8F6BB891D92 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = "<group>"; };
8BF798509C76E5A9ACE03491 /* Pods_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5A3FFC01B2145C4BFD8316A /* ParseBoolTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseBoolTests.swift; sourceTree = "<group>"; };
DD0B6D5E1EDF7C2100C8862C /* ParseEnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseEnumTests.swift; sourceTree = "<group>"; };
DD4393FE1E2D3EEB0047A332 /* MapNodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapNodeTests.swift; sourceTree = "<group>"; };
DD5F341A1E21993A00AEB5DA /* TestsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestsHelper.swift; sourceTree = "<group>"; };
DD5F34201E2199ED00AEB5DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -150,6 +152,7 @@
DD5F342C1E21A3A200AEB5DA /* StringFiltersTests.swift */,
DD5F342D1E21A3A200AEB5DA /* SwiftIdentifierTests.swift */,
B5A3FFC01B2145C4BFD8316A /* ParseBoolTests.swift */,
DD0B6D5E1EDF7C2100C8862C /* ParseEnumTests.swift */,
DD5F341C1E2199ED00AEB5DA /* Resources */,
);
path = StencilSwiftKitTests;
Expand Down Expand Up @@ -295,6 +298,7 @@
files = (
DD5F342F1E21A3A200AEB5DA /* SetNodeTests.swift in Sources */,
DD5F34311E21A3A200AEB5DA /* SwiftIdentifierTests.swift in Sources */,
DD0B6D5F1EDF7C2100C8862C /* ParseEnumTests.swift in Sources */,
DDE1E2F61E3E33E30043367C /* MacroNodeTests.swift in Sources */,
DD4393FF1E2D3EEB0047A332 /* MapNodeTests.swift in Sources */,
DD5F341B1E21993A00AEB5DA /* TestsHelper.swift in Sources */,
Expand Down
55 changes: 21 additions & 34 deletions Tests/StencilSwiftKitTests/ParseBoolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,68 +9,55 @@ import XCTest

class ParseBoolTests: XCTestCase {

func testParseBool_WithTrueString() throws {
let value = try Filters.parseBool(from: ["true"], index: 0)
XCTAssertTrue(value!)
func testParseBool_TrueWithString() throws {
XCTAssertTrue(try Filters.parseBool(from: ["true"])!)
XCTAssertTrue(try Filters.parseBool(from: ["yes"])!)
XCTAssertTrue(try Filters.parseBool(from: ["1"])!)
}

func testParseBool_WithFalseString() throws {
let value = try Filters.parseBool(from: ["false"], index: 0)
XCTAssertFalse(value!)
}

func testParseBool_WithYesString() throws {
let value = try Filters.parseBool(from: ["yes"], index: 0)
XCTAssertTrue(value!)
}

func testParseBool_WithNoString() throws {
let value = try Filters.parseBool(from: ["no"], index: 0)
XCTAssertFalse(value!)
}

func testParseBool_WithOneString() throws {
let value = try Filters.parseBool(from: ["1"], index: 0)
XCTAssertTrue(value!)
}

func testParseBool_WithZeroString() throws {
let value = try Filters.parseBool(from: ["0"], index: 0)
XCTAssertFalse(value!)
func testParseBool_FalseWithString() throws {
XCTAssertFalse(try Filters.parseBool(from: ["false"])!)
XCTAssertFalse(try Filters.parseBool(from: ["no"])!)
XCTAssertFalse(try Filters.parseBool(from: ["0"])!)
}

func testParseBool_WithOptionalInt() throws {
let value = try Filters.parseBool(from: [1], index: 0, required: false)
let value = try Filters.parseBool(from: [1], required: false)
XCTAssertNil(value)
}

func testParseBool_WithRequiredInt() throws {
XCTAssertThrowsError(try Filters.parseBool(from: [1], index: 0, required: true))
XCTAssertThrowsError(try Filters.parseBool(from: [1], required: true))
}

func testParseBool_WithOptionalDouble() throws {
let value = try Filters.parseBool(from: [1.0], index: 0, required: false)
let value = try Filters.parseBool(from: [1.0], required: false)
XCTAssertNil(value)
}

func testParseBool_WithRequiredDouble() throws {
XCTAssertThrowsError(try Filters.parseBool(from: [1.0], index: 0, required: true))
XCTAssertThrowsError(try Filters.parseBool(from: [1.0], required: true))
}

func testParseBool_WithEmptyString() throws {
XCTAssertThrowsError(try Filters.parseBool(from: [""], index: 0, required: false))
XCTAssertThrowsError(try Filters.parseBool(from: [""], required: false))
}

func testParseBool_WithEmptyStringAndRequiredArg() throws {
XCTAssertThrowsError(try Filters.parseBool(from: [""], index: 0, required: true))
XCTAssertThrowsError(try Filters.parseBool(from: [""], required: true))
}

func testParseBool_WithEmptyArray() throws {
let value = try Filters.parseBool(from: [], index: 0, required: false)
let value = try Filters.parseBool(from: [], required: false)
XCTAssertNil(value)
}

func testParseBool_WithEmptyArrayAndRequiredArg() throws {
XCTAssertThrowsError(try Filters.parseBool(from: [], index: 0, required: true))
XCTAssertThrowsError(try Filters.parseBool(from: [], required: true))
}

func testParseBool_WithNonZeroIndex() throws {
XCTAssertTrue(try Filters.parseBool(from: ["test", "true"], at: 1)!)
XCTAssertFalse(try Filters.parseBool(from: ["test", "false"], at: 1)!)
}
}
46 changes: 46 additions & 0 deletions Tests/StencilSwiftKitTests/ParseEnumTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// StencilSwiftKit
// Copyright (c) 2017 SwiftGen
// MIT Licence
//

import XCTest
@testable import StencilSwiftKit

class ParseEnumTests: XCTestCase {
enum Test: String {
case foo
case bar
case baz
}

func testParseEnum_WithFooString() throws {
let value = try Filters.parseEnum(from: ["foo"], default: Test.baz)
XCTAssertEqual(value, Test.foo)
}

func testParseEnum_WithBarString() throws {
let value = try Filters.parseEnum(from: ["bar"], default: Test.baz)
XCTAssertEqual(value, Test.bar)
}

func testParseEnum_WithBazString() throws {
let value = try Filters.parseEnum(from: ["baz"], default: Test.baz)
XCTAssertEqual(value, Test.baz)
}

func testParseEnum_WithEmptyArray() throws {
let value = try Filters.parseEnum(from: [], default: Test.baz)
XCTAssertEqual(value, Test.baz)
}

func testParseEnum_WithNonZeroIndex() throws {
let value = try Filters.parseEnum(from: [42, "bar"], at: 1, default: Test.baz)
XCTAssertEqual(value, Test.bar)
}

func testParseEnum_WithUnknownArgument() throws {
XCTAssertThrowsError(try Filters.parseEnum(from: ["test"], default: Test.baz))
XCTAssertThrowsError(try Filters.parseEnum(from: [42], default: Test.baz))
}
}
Loading

0 comments on commit 4a73848

Please sign in to comment.