Skip to content
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

Filter: Only remove multiple spaces #48

Merged
merged 4 commits into from
Jun 4, 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
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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a test with a non-zero index argument too

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