diff --git a/.github/workflows/cocoapods.yml b/.github/workflows/cocoapods.yml index 0c58b11..f019d45 100644 --- a/.github/workflows/cocoapods.yml +++ b/.github/workflows/cocoapods.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml index 2d87473..3bcb88e 100644 --- a/.github/workflows/swiftlint.yml +++ b/.github/workflows/swiftlint.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/test-spm.yml b/.github/workflows/test-spm.yml index f39b84a..9428a3e 100644 --- a/.github/workflows/test-spm.yml +++ b/.github/workflows/test-spm.yml @@ -9,27 +9,27 @@ jobs: linux: name: Test SPM Linux runs-on: ubuntu-latest - container: swiftgen/swift:5.4 + container: swiftgen/swift:5.6 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - # Note: we can't use `ruby/setup-ruby` on custom docker images, so we # have to do our own caching name: Cache gems - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gems- - name: Cache SPM - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: .build - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm-${{ hashFiles('Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- - @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Ruby uses: ruby/setup-ruby@v1 @@ -55,10 +55,10 @@ jobs: bundler-cache: true - name: Cache SPM - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: .build - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} + key: ${{ runner.os }}-spm-${{ hashFiles('Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- - diff --git a/.ruby-version b/.ruby-version index 338a5b5..b0f2dcb 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.6 +3.0.4 diff --git a/.swiftlint.yml b/.swiftlint.yml index 367f041..b17cbb2 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,8 @@ -swiftlint_version: 0.44.0 +swiftlint_version: 0.47.1 opt_in_rules: + - accessibility_label_for_image + - anonymous_argument_in_multiline_closure - anyobject_protocol - array_init - attributes @@ -31,6 +33,7 @@ opt_in_rules: - first_where - flatmap_over_map_reduce - force_unwrapping + - ibinspectable_in_extension - identical_operands - implicit_return - implicitly_unwrapped_optional @@ -43,6 +46,7 @@ opt_in_rules: - legacy_random - literal_expression_end_indentation - lower_acl_than_parent + - missing_docs - modifier_order - multiline_arguments - multiline_arguments_brackets @@ -57,8 +61,11 @@ opt_in_rules: - optional_enum_case_matching - overridden_super_call - override_in_extension + - pattern_matching_keywords + - prefer_self_in_static_references - prefer_self_type_over_type_of_self - prefer_zero_over_explicit_init + - prefixed_toplevel_constant - private_action - private_outlet - private_subject @@ -66,9 +73,14 @@ opt_in_rules: - raw_value_for_camel_cased_codable_enum - reduce_into - redundant_nil_coalescing + - redundant_type_annotation + - required_enum_case + - return_value_from_void_function + - single_test_class - sorted_first_last - sorted_imports - static_operator + - strict_fileprivate - strong_iboutlet - switch_case_on_newline - test_case_accessibility @@ -81,6 +93,7 @@ opt_in_rules: - vertical_parameter_alignment_on_call - vertical_whitespace_closing_braces - vertical_whitespace_opening_braces + - weak_delegate - xct_specific_matcher - yoda_condition diff --git a/CHANGELOG.md b/CHANGELOG.md index 56da4b1..8571dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,10 @@ _None_ ### Internal Changes -* Update to SwiftLint 0.43.1 and enable some extra SwiftLint rules. +* Update to SwiftLint 0.47.1 and enable some extra SwiftLint rules. [David Jennes](https://github.com/djbe) [#140](https://github.com/SwiftGen/StencilSwiftKit/pull/140) + [#153](https://github.com/SwiftGen/StencilSwiftKit/pull/153) ## 2.8.0 diff --git a/Gemfile b/Gemfile index 39ee16c..91b6279 100644 --- a/Gemfile +++ b/Gemfile @@ -5,13 +5,13 @@ source 'https://rubygems.org' # The bare minimum for building, e.g. in Homebrew group :build do gem 'rake', '~> 13.0' - gem 'xcpretty' + gem 'xcpretty', '~> 0.3' end # In addition to :build, for contributing group :development do gem 'cocoapods', '~> 1.11' - gem 'rubocop', '~> 1.20' + gem 'rubocop', '~> 1.22' end # For releasing to GitHub diff --git a/Gemfile.lock b/Gemfile.lock index 41f63c4..f6cfe45 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,9 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (6.1.4.1) + CFPropertyList (3.0.5) + rexml + activesupport (6.1.6.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -15,11 +16,11 @@ GEM json (>= 1.5.1) ast (2.4.2) atomos (0.1.3) - claide (1.0.3) - cocoapods (1.11.0) + claide (1.1.0) + cocoapods (1.11.3) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.11.0) + cocoapods-core (= 1.11.3) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -34,7 +35,7 @@ GEM nap (~> 1.0) ruby-macho (>= 1.0, < 3.0) xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.11.0) + cocoapods-core (1.11.3) activesupport (>= 5.0, < 7) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -45,7 +46,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.5.1) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -54,78 +55,63 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) escape (0.0.4) - ethon (0.14.0) + ethon (0.15.0) ffi (>= 1.15.0) - faraday (1.7.1) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday (2.3.0) + faraday-net_http (~> 2.0) ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - ffi (1.15.4) + faraday-net_http (2.0.3) + ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.8.10) + i18n (1.12.0) concurrent-ruby (~> 1.0) - json (2.5.1) - minitest (5.14.4) + json (2.6.2) + minitest (5.16.2) molinillo (0.8.0) - multipart-post (2.1.1) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - octokit (4.21.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) - parallel (1.20.1) - parser (3.0.2.0) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + parallel (1.22.1) + parser (3.1.2.0) ast (~> 2.4.1) - public_suffix (4.0.6) - rainbow (3.0.0) + public_suffix (4.0.7) + rainbow (3.1.1) rake (13.0.6) - regexp_parser (2.1.1) + regexp_parser (2.5.0) rexml (3.2.5) rouge (2.0.7) - rubocop (1.20.0) + rubocop (1.32.0) + json (~> 2.3) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.9.1, < 2.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.19.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.11.0) - parser (>= 3.0.1.1) + rubocop-ast (1.19.1) + parser (>= 3.1.1.0) ruby-macho (2.5.1) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) - sawyer (0.8.2) + sawyer (0.9.2) addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) + faraday (>= 0.17.3, < 3) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (2.0.4) + tzinfo (2.0.5) concurrent-ruby (~> 1.0) - unicode-display_width (2.0.0) - xcodeproj (1.21.0) + unicode-display_width (2.2.0) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -134,7 +120,7 @@ GEM rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) - zeitwerk (2.4.2) + zeitwerk (2.6.0) PLATFORMS ruby @@ -143,8 +129,8 @@ DEPENDENCIES cocoapods (~> 1.11) octokit (~> 4.21) rake (~> 13.0) - rubocop (~> 1.20) - xcpretty + rubocop (~> 1.22) + xcpretty (~> 0.3) BUNDLED WITH - 2.2.27 + 2.2.33 diff --git a/Package.resolved b/Package.resolved index 87ad1f8..36ce673 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,17 @@ "repositoryURL": "https://github.com/shibapm/Komondor.git", "state": { "branch": null, - "revision": "855c74f395a4dc9e02828f58d931be6920bcbf6f", - "version": "1.0.6" + "revision": "dedffeaf42a1b52bf072a9dc47afb35000a8b265", + "version": "1.1.4" + } + }, + { + "package": "Logger", + "repositoryURL": "https://github.com/shibapm/Logger", + "state": { + "branch": null, + "revision": "53c3ecca5abe8cf46697e33901ee774236d94cce", + "version": "0.2.3" } }, { @@ -15,8 +24,8 @@ "repositoryURL": "https://github.com/shibapm/PackageConfig.git", "state": { "branch": null, - "revision": "bf90dc69fa0792894b08a0b74cf34029694ae486", - "version": "0.13.0" + "revision": "58523193c26fb821ed1720dcd8a21009055c7cdb", + "version": "1.1.3" } }, { @@ -28,6 +37,15 @@ "version": "1.0.0" } }, + { + "package": "Rocket", + "repositoryURL": "https://github.com/f-meloni/Rocket", + "state": { + "branch": null, + "revision": "9880a5beb7fcb9e61ddd5764edc1700b8c418deb", + "version": "1.2.1" + } + }, { "package": "ShellOut", "repositoryURL": "https://github.com/JohnSundell/ShellOut.git", @@ -37,6 +55,15 @@ "version": "2.3.0" } }, + { + "package": "SourceKitten", + "repositoryURL": "https://github.com/jpsim/SourceKitten.git", + "state": { + "branch": null, + "revision": "817dfa6f2e09b0476f3a6c9dbc035991f02f0241", + "version": "0.32.0" + } + }, { "package": "Spectre", "repositoryURL": "https://github.com/kylef/Spectre.git", @@ -54,6 +81,78 @@ "revision": "94197b3adbbf926348ad8765476a158aa4e54f8a", "version": "0.14.0" } + }, + { + "package": "swift-argument-parser", + "repositoryURL": "https://github.com/apple/swift-argument-parser.git", + "state": { + "branch": null, + "revision": "e394bf350e38cb100b6bc4172834770ede1b7232", + "version": "1.0.3" + } + }, + { + "package": "SwiftSyntax", + "repositoryURL": "https://github.com/apple/swift-syntax.git", + "state": { + "branch": null, + "revision": "0b6c22b97f8e9320bca62e82cdbee601cf37ad3f", + "version": "0.50600.1" + } + }, + { + "package": "SwiftFormat", + "repositoryURL": "https://github.com/nicklockwood/SwiftFormat.git", + "state": { + "branch": null, + "revision": "665c3c58923ee8ac36d3e44b17dc185229cce301", + "version": "0.49.13" + } + }, + { + "package": "SwiftLint", + "repositoryURL": "https://github.com/Realm/SwiftLint.git", + "state": { + "branch": null, + "revision": "e497f1f5b161af96ba439049d21970c6204d06c6", + "version": "0.47.1" + } + }, + { + "package": "SwiftShell", + "repositoryURL": "https://github.com/kareman/SwiftShell", + "state": { + "branch": null, + "revision": "a6014fe94c3dbff0ad500e8da4f251a5d336530b", + "version": "5.1.0-beta.1" + } + }, + { + "package": "SwiftyTextTable", + "repositoryURL": "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state": { + "branch": null, + "revision": "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version": "0.9.0" + } + }, + { + "package": "SWXMLHash", + "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", + "state": { + "branch": null, + "revision": "6469881a3f30417c5bb02404ea4b69207f297592", + "version": "6.0.0" + } + }, + { + "package": "Yams", + "repositoryURL": "https://github.com/jpsim/Yams", + "state": { + "branch": null, + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + } } ] }, diff --git a/Package.swift b/Package.swift index fda7261..b3bae34 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ let package = Package( .library(name: "StencilSwiftKit", targets: ["StencilSwiftKit"]) ], dependencies: [ - .package(url: "https://github.com/shibapm/Komondor.git", from: "1.0.0"), + .package(url: "https://github.com/shibapm/Komondor.git", from: "1.1.3"), .package(url: "https://github.com/stencilproject/Stencil.git", .upToNextMajor(from: "0.14.0")) ], targets: [ diff --git a/Rakefile b/Rakefile index d52fd91..99882cf 100755 --- a/Rakefile +++ b/Rakefile @@ -9,7 +9,7 @@ end ## [ Constants ] ############################################################## POD_NAME = 'StencilSwiftKit'.freeze -MIN_XCODE_VERSION = 12.0 +MIN_XCODE_VERSION = 13.0 BUILD_DIR = File.absolute_path('./.build') ## [ Release a new version ] ################################################## diff --git a/Sources/StencilSwiftKit/CallMacroNodes.swift b/Sources/StencilSwiftKit/CallMacroNodes.swift index e214d82..76441c8 100644 --- a/Sources/StencilSwiftKit/CallMacroNodes.swift +++ b/Sources/StencilSwiftKit/CallMacroNodes.swift @@ -6,7 +6,7 @@ import Stencil -struct CallableBlock { +internal struct CallableBlock { let parameters: [String] let nodes: [NodeType] @@ -34,7 +34,7 @@ struct CallableBlock { } } -class MacroNode: NodeType { +internal class MacroNode: NodeType { let variableName: String let parameters: [String] let nodes: [NodeType] @@ -70,7 +70,7 @@ class MacroNode: NodeType { } } -class CallNode: NodeType { +internal class CallNode: NodeType { let variable: Variable let arguments: [Resolvable] let token: Token? @@ -82,8 +82,8 @@ class CallNode: NodeType { } let variable = Variable(components[1]) - let arguments = try Array(components.dropFirst(2)).map { - try parser.compileResolvable($0, containedIn: token) + let arguments = try Array(components.dropFirst(2)).map { part in + try parser.compileResolvable(part, containedIn: token) } return CallNode(variable: variable, arguments: arguments, token: token) diff --git a/Sources/StencilSwiftKit/Context.swift b/Sources/StencilSwiftKit/Context.swift index 06ace41..29118c1 100644 --- a/Sources/StencilSwiftKit/Context.swift +++ b/Sources/StencilSwiftKit/Context.swift @@ -6,8 +6,11 @@ import Foundation +/// Helper for enriching a Stencil context with environment or parameters public enum StencilContext { + /// Stencil context key where environment data will be set public static let environmentKey = "env" + /// Stencil context key where parameter data will be set public static let parametersKey = "param" /// Enriches a stencil context with parsed parameters and environment variables diff --git a/Sources/StencilSwiftKit/Environment.swift b/Sources/StencilSwiftKit/Environment.swift index 81b72f5..c956cd1 100644 --- a/Sources/StencilSwiftKit/Environment.swift +++ b/Sources/StencilSwiftKit/Environment.swift @@ -7,15 +7,16 @@ import Stencil public extension Extension { + /// Registers this package's tags and filters func registerStencilSwiftExtensions() { registerTags() registerStringsFilters() registerNumbersFilters() } +} - // MARK: - Private - - private func registerFilter(_ name: String, filter: @escaping Filters.BooleanWithArguments) { +private extension Extension { + func registerFilter(_ name: String, filter: @escaping Filters.BooleanWithArguments) { typealias GenericFilter = (Any?, [Any?]) throws -> Any? let inverseFilter: GenericFilter = { value, arguments in try !filter(value, arguments) @@ -24,13 +25,13 @@ public extension Extension { registerFilter("!\(name)", filter: inverseFilter) } - private func registerNumbersFilters() { + func registerNumbersFilters() { registerFilter("hexToInt", filter: Filters.Numbers.hexToInt) registerFilter("int255toFloat", filter: Filters.Numbers.int255toFloat) registerFilter("percent", filter: Filters.Numbers.percent) } - private func registerStringsFilters() { + func registerStringsFilters() { registerFilter("basename", filter: Filters.Strings.basename) registerFilter("camelToSnakeCase", filter: Filters.Strings.camelToSnakeCase) registerFilter("dirname", filter: Filters.Strings.dirname) @@ -48,7 +49,7 @@ public extension Extension { registerFilter("hasSuffix", filter: Filters.Strings.hasSuffix) } - private func registerTags() { + func registerTags() { registerTag("set", parser: SetNode.parse) registerTag("macro", parser: MacroNode.parse) registerTag("call", parser: CallNode.parse) @@ -56,6 +57,9 @@ public extension Extension { } } +/// Creates an Environment for Stencil to load & render templates +/// +/// - Returns: A fully configured `Environment` public func stencilSwiftEnvironment() -> Environment { let ext = Extension() ext.registerStencilSwiftExtensions() diff --git a/Sources/StencilSwiftKit/Filters+Numbers.swift b/Sources/StencilSwiftKit/Filters+Numbers.swift index 0815481..4b9f715 100644 --- a/Sources/StencilSwiftKit/Filters+Numbers.swift +++ b/Sources/StencilSwiftKit/Filters+Numbers.swift @@ -8,17 +8,36 @@ import Foundation import Stencil public extension Filters { + /// Filters for operations related to numbers enum Numbers { + /// Tries to parse the given `String` into an `Int` using radix 16. + /// + /// - Parameters: + /// - value: the string value to parse + /// - Returns: the parsed `Int` + /// - Throws: Filters.Error.invalidInputType if the value parameter isn't a string public static func hexToInt(_ value: Any?) throws -> Any? { guard let value = value as? String else { throw Filters.Error.invalidInputType } return Int(value, radix: 16) } + /// Tries to convert the given `Int` to a `Float` by dividing it by 255. + /// + /// - Parameters: + /// - value: the `Int` value to convert + /// - Returns: the convert `Float` + /// - Throws: Filters.Error.invalidInputType if the value parameter isn't an integer public static func int255toFloat(_ value: Any?) throws -> Any? { guard let value = value as? Int else { throw Filters.Error.invalidInputType } return Float(value) / Float(255.0) } + /// Tries to convert the given `Float` to a percentage string `…%`, after multiplying by 100. + /// + /// - Parameters: + /// - value: the `Float` value to convert + /// - Returns: the rendered `String` + /// - Throws: Filters.Error.invalidInputType if the value parameter isn't a float public static func percent(_ value: Any?) throws -> Any? { guard let value = value as? Float else { throw Filters.Error.invalidInputType } diff --git a/Sources/StencilSwiftKit/Filters+Strings.swift b/Sources/StencilSwiftKit/Filters+Strings.swift index f84ed19..d5ede8a 100644 --- a/Sources/StencilSwiftKit/Filters+Strings.swift +++ b/Sources/StencilSwiftKit/Filters+Strings.swift @@ -11,14 +11,17 @@ import Stencil // MARK: - Strings Filters public extension Filters { + /// Filters for operations related to strings enum Strings { } } +/// Possible modes for removeNewlines filter public enum RemoveNewlinesModes: String { case all, leading } +/// Possible modes for swiftIdentifier filter public enum SwiftIdentifierModes: String { case normal, pretty } @@ -229,6 +232,12 @@ private extension Filters.Strings { // MARK: - String Filters: Mutation filters public extension Filters.Strings { + /// Tries to parse the value as a `String`, and then checks if the string is one of the reserved keywords. + /// If so, escapes it using backticks + /// + /// - Parameter in: the string to possibly escape + /// - Returns: if the string is a reserved keyword, the escaped string, otherwise the original one + /// - Throws: Filters.Error.invalidInputType static func escapeReservedKeywords(value: Any?) throws -> Any? { let string = try Filters.parseString(from: value) return escapeReservedKeywords(in: string) diff --git a/Sources/StencilSwiftKit/Filters.swift b/Sources/StencilSwiftKit/Filters.swift index 93bfa85..1054380 100644 --- a/Sources/StencilSwiftKit/Filters.swift +++ b/Sources/StencilSwiftKit/Filters.swift @@ -7,9 +7,11 @@ import Foundation import Stencil +/// Namespace for filters public enum Filters { typealias BooleanWithArguments = (Any?, [Any?]) throws -> Bool + /// Possible filter errors public enum Error: Swift.Error { case invalidInputType case invalidOption(option: String) @@ -58,6 +60,7 @@ public enum Filters { throw Error.invalidInputType } + // swiftlint:disable discouraged_optional_boolean /// Parses filter arguments for a boolean value, where true can by any one of: "true", "yes", "1", and /// false can be any one of: "false", "no", "0". If optional is true it means that the argument on the filter is /// optional and it's not an error condition if the argument is missing or not the right type @@ -68,7 +71,6 @@ public 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 - // swiftlint:disable discouraged_optional_boolean public 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 { @@ -105,7 +107,7 @@ public enum Filters { let arg = arguments[index].map(String.init(describing:)) ?? `default`.rawValue guard let result = T(rawValue: arg) else { - throw Filters.Error.invalidOption(option: arg) + throw Self.Error.invalidOption(option: arg) } return result diff --git a/Sources/StencilSwiftKit/MapNode.swift b/Sources/StencilSwiftKit/MapNode.swift index 1ed5a9f..d1c1d28 100644 --- a/Sources/StencilSwiftKit/MapNode.swift +++ b/Sources/StencilSwiftKit/MapNode.swift @@ -6,7 +6,7 @@ import Stencil -class MapNode: NodeType { +internal class MapNode: NodeType { let resolvable: Resolvable let resultName: String let mapVariable: String? diff --git a/Sources/StencilSwiftKit/Parameters.swift b/Sources/StencilSwiftKit/Parameters.swift index 65c708b..5cca180 100644 --- a/Sources/StencilSwiftKit/Parameters.swift +++ b/Sources/StencilSwiftKit/Parameters.swift @@ -18,6 +18,7 @@ public enum Parameters { } typealias Parameter = (key: String, value: Any) + /// A string-keyed dictionary public typealias StringDict = [String: Any] /// Transforms a list of strings representing structured-key/value pairs, like @@ -32,8 +33,8 @@ public enum Parameters { /// - Throws: `Parameters.Error` public static func parse(items: [String]) throws -> StringDict { let parameters: [Parameter] = try items.map(createParameter) - return try parameters.reduce(StringDict()) { - try parse(parameter: $1, result: $0) + return try parameters.reduce(StringDict()) { result, parameter in + try parse(parameter: parameter, result: result) } } diff --git a/Sources/StencilSwiftKit/SetNode.swift b/Sources/StencilSwiftKit/SetNode.swift index 3dd4df6..6a6ef18 100644 --- a/Sources/StencilSwiftKit/SetNode.swift +++ b/Sources/StencilSwiftKit/SetNode.swift @@ -6,7 +6,7 @@ import Stencil -class SetNode: NodeType { +internal class SetNode: NodeType { enum Content { case nodes([NodeType]) case reference(resolvable: Resolvable) diff --git a/Sources/StencilSwiftKit/SwiftIdentifier.swift b/Sources/StencilSwiftKit/SwiftIdentifier.swift index 245fa74..09bb739 100644 --- a/Sources/StencilSwiftKit/SwiftIdentifier.swift +++ b/Sources/StencilSwiftKit/SwiftIdentifier.swift @@ -8,98 +8,86 @@ import Foundation typealias CharRange = CountableClosedRange -private func mr(_ char: Int) -> CharRange { - char...char -} - // Official list of valid identifier characters // swiftlint:disable:next line_length // from: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID410 -private let headRanges: [CharRange] = [ - 0x61...0x7a as CharRange, - 0x41...0x5a as CharRange, - mr(0x5f), mr(0xa8), mr(0xaa), mr(0xad), mr(0xaf), - 0xb2...0xb5 as CharRange, - 0xb7...0xba as CharRange, - 0xbc...0xbe as CharRange, - 0xc0...0xd6 as CharRange, - 0xd8...0xf6 as CharRange, - 0xf8...0xff as CharRange, - 0x100...0x2ff as CharRange, - 0x370...0x167f as CharRange, - 0x1681...0x180d as CharRange, - 0x180f...0x1dbf as CharRange, - 0x1e00...0x1fff as CharRange, - 0x200b...0x200d as CharRange, - 0x202a...0x202e as CharRange, - mr(0x203F), mr(0x2040), mr(0x2054), - 0x2060...0x206f as CharRange, - 0x2070...0x20cf as CharRange, - 0x2100...0x218f as CharRange, - 0x2460...0x24ff as CharRange, - 0x2776...0x2793 as CharRange, - 0x2c00...0x2dff as CharRange, - 0x2e80...0x2fff as CharRange, - 0x3004...0x3007 as CharRange, - 0x3021...0x302f as CharRange, - 0x3031...0x303f as CharRange, - 0x3040...0xd7ff as CharRange, - 0xf900...0xfd3d as CharRange, - 0xfd40...0xfdcf as CharRange, - 0xfdf0...0xfe1f as CharRange, - 0xfe30...0xfe44 as CharRange, - 0xfe47...0xfffd as CharRange, - 0x10000...0x1fffd as CharRange, - 0x20000...0x2fffd as CharRange, - 0x30000...0x3fffd as CharRange, - 0x40000...0x4fffd as CharRange, - 0x50000...0x5fffd as CharRange, - 0x60000...0x6fffd as CharRange, - 0x70000...0x7fffd as CharRange, - 0x80000...0x8fffd as CharRange, - 0x90000...0x9fffd as CharRange, - 0xa0000...0xafffd as CharRange, - 0xb0000...0xbfffd as CharRange, - 0xc0000...0xcfffd as CharRange, - 0xd0000...0xdfffd as CharRange, - 0xe0000...0xefffd as CharRange -] +private extension CharRange { + static func mr(_ char: Int) -> CharRange { + char...char + } -private let tailRanges: [CharRange] = [ - 0x30...0x39, 0x300...0x36F, 0x1dc0...0x1dff, 0x20d0...0x20ff, 0xfe20...0xfe2f -] + static let headRanges: [CharRange] = [ + 0x61...0x7a as CharRange, + 0x41...0x5a as CharRange, + mr(0x5f), mr(0xa8), mr(0xaa), mr(0xad), mr(0xaf), + 0xb2...0xb5 as CharRange, + 0xb7...0xba as CharRange, + 0xbc...0xbe as CharRange, + 0xc0...0xd6 as CharRange, + 0xd8...0xf6 as CharRange, + 0xf8...0xff as CharRange, + 0x100...0x2ff as CharRange, + 0x370...0x167f as CharRange, + 0x1681...0x180d as CharRange, + 0x180f...0x1dbf as CharRange, + 0x1e00...0x1fff as CharRange, + 0x200b...0x200d as CharRange, + 0x202a...0x202e as CharRange, + mr(0x203F), mr(0x2040), mr(0x2054), + 0x2060...0x206f as CharRange, + 0x2070...0x20cf as CharRange, + 0x2100...0x218f as CharRange, + 0x2460...0x24ff as CharRange, + 0x2776...0x2793 as CharRange, + 0x2c00...0x2dff as CharRange, + 0x2e80...0x2fff as CharRange, + 0x3004...0x3007 as CharRange, + 0x3021...0x302f as CharRange, + 0x3031...0x303f as CharRange, + 0x3040...0xd7ff as CharRange, + 0xf900...0xfd3d as CharRange, + 0xfd40...0xfdcf as CharRange, + 0xfdf0...0xfe1f as CharRange, + 0xfe30...0xfe44 as CharRange, + 0xfe47...0xfffd as CharRange, + 0x10000...0x1fffd as CharRange, + 0x20000...0x2fffd as CharRange, + 0x30000...0x3fffd as CharRange, + 0x40000...0x4fffd as CharRange, + 0x50000...0x5fffd as CharRange, + 0x60000...0x6fffd as CharRange, + 0x70000...0x7fffd as CharRange, + 0x80000...0x8fffd as CharRange, + 0x90000...0x9fffd as CharRange, + 0xa0000...0xafffd as CharRange, + 0xb0000...0xbfffd as CharRange, + 0xc0000...0xcfffd as CharRange, + 0xd0000...0xdfffd as CharRange, + 0xe0000...0xefffd as CharRange + ] -private func identifierCharacterSets(exceptions: String) -> (head: NSMutableCharacterSet, tail: NSMutableCharacterSet) { - let addRange: (NSMutableCharacterSet, CharRange) -> Void = { mcs, range in - mcs.addCharacters(in: NSRange(location: range.lowerBound, length: range.count)) - } + static let tailRanges: [CharRange] = [ + 0x30...0x39, 0x300...0x36F, 0x1dc0...0x1dff, 0x20d0...0x20ff, 0xfe20...0xfe2f + ] +} - let head = NSMutableCharacterSet() - for range in headRanges { - addRange(head, range) - } - head.removeCharacters(in: exceptions) +private extension CharacterSet { + static let illegalIdentifierHead = setFromRanges(CharRange.headRanges) + static let illegalIdentifierTail = setFromRanges(CharRange.headRanges + CharRange.tailRanges) - guard let tail = head.mutableCopy() as? NSMutableCharacterSet else { - fatalError("Internal error: mutableCopy() should have returned a valid NSMutableCharacterSet") - } - for range in tailRanges { - addRange(tail, range) + static func setFromRanges(_ ranges: [CharRange]) -> CharacterSet { + var result = CharacterSet() + for range in ranges { + guard let lower = Unicode.Scalar(range.lowerBound), let upper = Unicode.Scalar(range.upperBound) else { continue } + result.insert(charactersIn: lower...upper) + } + return result } - tail.removeCharacters(in: exceptions) - - return (head, tail) } enum SwiftIdentifier { - static func identifier( - from string: String, - forbiddenChars exceptions: String = "", - replaceWithUnderscores underscores: Bool = false - ) -> String { - let (_, tail) = identifierCharacterSets(exceptions: exceptions) - - let parts = string.components(separatedBy: tail.inverted) + static func identifier(from string: String, replaceWithUnderscores underscores: Bool = false) -> String { + let parts = string.components(separatedBy: CharacterSet.illegalIdentifierTail.inverted) let replacement = underscores ? "_" : "" let mappedParts = parts.map { (string: String) -> String in // Can't use capitalizedString here because it will lowercase all letters after the first @@ -109,17 +97,12 @@ enum SwiftIdentifier { } let result = mappedParts.joined(separator: replacement) - return prefixWithUnderscoreIfNeeded(string: result, forbiddenChars: exceptions) + return prefixWithUnderscoreIfNeeded(string: result) } - static func prefixWithUnderscoreIfNeeded( - string: String, - forbiddenChars exceptions: String = "" - ) -> String { + static func prefixWithUnderscoreIfNeeded(string: String) -> String { guard let firstChar = string.unicodeScalars.first else { return "" } - - let (head, _) = identifierCharacterSets(exceptions: exceptions) - let prefix = !head.longCharacterIsMember(firstChar.value) ? "_" : "" + let prefix = !CharacterSet.illegalIdentifierHead.contains(firstChar) ? "_" : "" return prefix + string } diff --git a/Tests/StencilSwiftKitTests/MapNodeTests.swift b/Tests/StencilSwiftKitTests/MapNodeTests.swift index aeafe5b..73b7348 100644 --- a/Tests/StencilSwiftKitTests/MapNodeTests.swift +++ b/Tests/StencilSwiftKitTests/MapNodeTests.swift @@ -145,7 +145,7 @@ final class MapNodeTests: XCTestCase { } func testRender() throws { - let context = Context(dictionary: MapNodeTests.context) + let context = Context(dictionary: Self.context) let node = MapNode( resolvable: Variable("items"), resultName: "result", @@ -158,7 +158,7 @@ final class MapNodeTests: XCTestCase { } func testContext() throws { - let context = Context(dictionary: MapNodeTests.context) + let context = Context(dictionary: Self.context) let node = MapNode( resolvable: Variable("items"), resultName: "result", @@ -171,12 +171,12 @@ final class MapNodeTests: XCTestCase { XCTFail("Unable to render map") return } - XCTAssertEqual(items, MapNodeTests.context["items"] ?? []) + XCTAssertEqual(items, Self.context["items"] ?? []) XCTAssertEqual(result, ["hello", "hello", "hello"]) } func testMapLoopContext() throws { - let context = Context(dictionary: MapNodeTests.context) + let context = Context(dictionary: Self.context) let node = MapNode( resolvable: Variable("items"), resultName: "result", @@ -194,7 +194,7 @@ final class MapNodeTests: XCTestCase { XCTFail("Unable to render map") return } - XCTAssertEqual(items, MapNodeTests.context["items"] ?? []) + XCTAssertEqual(items, Self.context["items"] ?? []) XCTAssertEqual(result, ["0truefalseone", "1falsefalsetwo", "2falsetruethree"]) } } diff --git a/Tests/StencilSwiftKitTests/ParametersTests.swift b/Tests/StencilSwiftKitTests/ParametersTests.swift index e3c14ae..cb0c66f 100644 --- a/Tests/StencilSwiftKitTests/ParametersTests.swift +++ b/Tests/StencilSwiftKitTests/ParametersTests.swift @@ -93,9 +93,9 @@ final class ParametersTests: XCTestCase { // invalid character do { let items = ["foo:1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidSyntax = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidSyntax = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -104,9 +104,9 @@ final class ParametersTests: XCTestCase { // invalid character do { let items = ["foo!1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidSyntax = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidSyntax = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -115,9 +115,9 @@ final class ParametersTests: XCTestCase { // cannot be empty do { let items = [""] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidSyntax = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidSyntax = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -128,9 +128,9 @@ final class ParametersTests: XCTestCase { // key may only be alphanumeric or '.' do { let items = ["foo:bar=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidKey = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidKey = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -139,9 +139,9 @@ final class ParametersTests: XCTestCase { // can't have empty key or sub-key do { let items = [".=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidKey = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidKey = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -150,9 +150,9 @@ final class ParametersTests: XCTestCase { // can't have empty sub-key do { let items = ["foo.=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidKey = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidKey = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -163,9 +163,9 @@ final class ParametersTests: XCTestCase { // can't switch from string to dictionary do { let items = ["foo=1", "foo.bar=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidStructure = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidStructure = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -174,9 +174,9 @@ final class ParametersTests: XCTestCase { // can't switch from dictionary to array do { let items = ["foo.bar=1", "foo=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidStructure = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidStructure = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } @@ -185,9 +185,9 @@ final class ParametersTests: XCTestCase { // can't switch from array to dictionary do { let items = ["foo=1", "foo=2", "foo.bar=1"] - XCTAssertThrowsError(try Parameters.parse(items: items)) { - guard case Parameters.Error.invalidStructure = $0 else { - XCTFail("Unexpected error occured while parsing: \($0)") + XCTAssertThrowsError(try Parameters.parse(items: items)) { error in + guard case Parameters.Error.invalidStructure = error else { + XCTFail("Unexpected error occured while parsing: \(error)") return } } diff --git a/Tests/StencilSwiftKitTests/ParseStringTests.swift b/Tests/StencilSwiftKitTests/ParseStringTests.swift index b3a95b3..f1eff25 100644 --- a/Tests/StencilSwiftKitTests/ParseStringTests.swift +++ b/Tests/StencilSwiftKitTests/ParseStringTests.swift @@ -12,7 +12,7 @@ final class ParseStringTests: XCTestCase { static let stringRepresentation = "TestLosslessConvertibleStringRepresentation" var description: String { - TestLosslessConvertible.stringRepresentation + Self.stringRepresentation } init() {} @@ -23,7 +23,7 @@ final class ParseStringTests: XCTestCase { static let stringRepresentation = "TestConvertibleStringRepresentation" var description: String { - TestConvertible.stringRepresentation + Self.stringRepresentation } } diff --git a/Tests/StencilSwiftKitTests/SwiftIdentifierTests.swift b/Tests/StencilSwiftKitTests/SwiftIdentifierTests.swift index 2862714..dd70bed 100644 --- a/Tests/StencilSwiftKitTests/SwiftIdentifierTests.swift +++ b/Tests/StencilSwiftKitTests/SwiftIdentifierTests.swift @@ -12,17 +12,6 @@ final class SwiftIdentifierTests: XCTestCase { XCTAssertEqual(SwiftIdentifier.identifier(from: "Hello"), "Hello") } - func testBasicStringWithForbiddenChars() { - XCTAssertEqual(SwiftIdentifier.identifier(from: "Hello", forbiddenChars: "l"), "HeO") - } - - func testBasicStringWithForbiddenCharsAndUnderscores() { - XCTAssertEqual( - SwiftIdentifier.identifier(from: "Hello", forbiddenChars: "l", replaceWithUnderscores: true), - "He__O" - ) - } - func testSpecialChars() { XCTAssertEqual(SwiftIdentifier.identifier(from: "This-is-42$hello@world"), "ThisIs42HelloWorld") } diff --git a/rakelib/lint.rake b/rakelib/lint.rake index 31881e8..726d7d5 100644 --- a/rakelib/lint.rake +++ b/rakelib/lint.rake @@ -3,7 +3,7 @@ namespace :lint do SWIFTLINT = 'Scripts/SwiftLint.sh' - SWIFTLINT_VERSION = '0.44.0' + SWIFTLINT_VERSION = '0.47.1' task :install do |task| next if check_version diff --git a/rakelib/spm.rake b/rakelib/spm.rake index a8ec3ba..0475441 100644 --- a/rakelib/spm.rake +++ b/rakelib/spm.rake @@ -6,12 +6,12 @@ namespace :spm do desc 'Build using SPM' task :build do |task| Utils.print_header 'Compile using SPM' - Utils.run('swift build --enable-test-discovery', task, xcrun: true) + Utils.run('swift build', task, xcrun: true) end desc 'Run SPM Unit Tests' task :test => :build do |task| Utils.print_header 'Run the unit tests using SPM' - Utils.run('swift test --enable-test-discovery --parallel', task, xcrun: true) + Utils.run('swift test --parallel', task, xcrun: true) end end