├── .gitignore ├── StringTemplate.xcodeproj ├── xcshareddata │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── StringTemplate-Package.xcscheme ├── StringTemplate_Info.plist ├── StringTemplateTests_Info.plist └── project.pbxproj ├── Contributing.md ├── Package.swift ├── StringTemplate.podspec ├── Sources ├── CharacterSet+Character.swift ├── String+CharacterSet.swift └── String+Template.swift ├── README.md ├── Tests └── StringTemplateTests.swift └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | 5 | 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | *.xcworkspace 15 | !default.xcworkspace 16 | xcuserdata 17 | profile 18 | 19 | /Carthage 20 | -------------------------------------------------------------------------------- /StringTemplate.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | StringTemplate-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | ### Sign the CLA 2 | 3 | All contributors to your PR must sign our [Individual Contributor License 4 | Agreement (CLA)][CLA]. The CLA is a short form that ensures that you are 5 | eligible to contribute. 6 | 7 | [CLA]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 8 | 9 | ### Tests 10 | 11 | We require that any new features or bug fixes also include unit tests to cover 12 | the changes. 13 | -------------------------------------------------------------------------------- /StringTemplate.xcodeproj/StringTemplate_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "StringTemplate", 8 | platforms: [ 9 | .iOS(.v11), 10 | .macOS(.v10_13), 11 | .tvOS(.v11), 12 | .watchOS(.v4), 13 | ], 14 | products: [ 15 | .library( 16 | name: "StringTemplate", 17 | targets: ["StringTemplate"] 18 | ), 19 | ], 20 | targets: [ 21 | .target( 22 | name: "StringTemplate", 23 | dependencies: [], 24 | path: "Sources" 25 | ), 26 | .testTarget( 27 | name: "StringTemplateTests", 28 | dependencies: ["StringTemplate"], 29 | path: "Tests" 30 | ), 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /StringTemplate.xcodeproj/StringTemplateTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /StringTemplate.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'StringTemplate' 3 | spec.version = '2.2.0' 4 | spec.license = 'Apache License, Version 2.0' 5 | spec.homepage = 'https://github.com/square/StringTemplate' 6 | spec.authors = 'Square' 7 | spec.summary = 'String templating for Swift made easy.' 8 | spec.source = { :git => 'https://github.com/square/StringTemplate.git', :tag => "v#{spec.version}" } 9 | spec.source_files = 'Sources/*.swift' 10 | spec.swift_versions = ['4.0', '5.0'] 11 | 12 | spec.requires_arc = true 13 | spec.compiler_flags = '-whole-module-optimization' 14 | 15 | spec.ios.deployment_target = '11.0' 16 | spec.osx.deployment_target = '10.13' 17 | spec.watchos.deployment_target = '4.0' 18 | spec.tvos.deployment_target = '11.0' 19 | 20 | spec.pod_target_xcconfig = { 21 | 'APPLICATION_EXTENSION_API_ONLY' => 'YES', 22 | } 23 | end 24 | -------------------------------------------------------------------------------- /Sources/CharacterSet+Character.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2018 Square, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import Foundation 18 | 19 | extension CharacterSet { 20 | /** 21 | Create a `CharacterSet` from a single character 22 | */ 23 | init(character: Character) { 24 | self.init(charactersIn: String(character)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/String+CharacterSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2018 Square, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import Foundation 18 | 19 | extension String { 20 | /** 21 | Remove the characters in the provided `CharacterSet` from `self`. 22 | 23 | - parameter characterSet: The `CharacterSet` containing the characters 24 | that should be removed from `self`. 25 | */ 26 | mutating func removeCharacters(in characterSet: CharacterSet) { 27 | var nextSearchRange = range(of: self) 28 | 29 | while let searchRange = nextSearchRange, !searchRange.isEmpty { 30 | guard var characterRange = rangeOfCharacter(from: characterSet, options: .literal, range: searchRange) else { 31 | break 32 | } 33 | 34 | // Make sure not to break up composed character sequences when removing content. 35 | characterRange = rangeOfComposedCharacterSequences(for: characterRange) 36 | removeSubrange(characterRange) 37 | 38 | nextSearchRange = characterRange.lowerBound ..< endIndex 39 | } 40 | } 41 | 42 | /** 43 | Remove the characters in the provided `CharacterSet` from the callee, returning a new instance. 44 | 45 | This is an immutable variant of `removeCharacters(in:)` 46 | 47 | - parameter characterSet: The `CharacterSet` containing the characters 48 | that should be removed from the new string. 49 | - returns: A copy of `self` with the specified characters removed 50 | */ 51 | func removingCharacters(in characterSet: CharacterSet) -> String { 52 | var copy = self 53 | copy.removeCharacters(in: characterSet) 54 | return copy 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /StringTemplate.xcodeproj/xcshareddata/xcschemes/StringTemplate-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StringTemplate 2 | 3 | Quickly and easily apply a template to a target string. 4 | 5 | This library allows you to create light-weight templates and then arbitrarily 6 | apply them to string instances. You can also pass options that will let you 7 | customize the strictness with which a template is applied. 8 | 9 | ## Usage 10 | 11 | ```swift 12 | // Create a template: 13 | let nanpPhoneNumber = String.Template( 14 | placeholderToken: "X", 15 | template: "(XXX) XXX-XXXX" 16 | ) 17 | 18 | // And apply it: 19 | let formatted = try? "5125550001".applying(template: nanpPhoneNumber) 20 | print(formatted) // Optional("(512) 555-0001") 21 | 22 | // Allow for partial application: 23 | let partial = try? "512555".applying(template: nanpPhoneNumber, options: [.allowPartial]) 24 | print(partial) // Optional("(512) 555-") 25 | 26 | // Allow for overflow application: 27 | let overflow = try? "5125550001111111111".applying(template: nanpPhoneNumber, options: [.allowOverflow]) 28 | print(overflow) // Optional("(512) 555-0001") 29 | 30 | // Mutate the string directly: 31 | var str = "5125550001" 32 | try? str.apply(template: nanpPhoneNumber) 33 | print(str) // "(512) 555-0001" 34 | ``` 35 | 36 | See [the tests] for more examples. 37 | 38 | [the tests]: Tests/StringTemplateTests.swift 39 | 40 | ## Installation 41 | 42 | ### [Swift Package Manager] 43 | 44 | [Swift Package Manager]: https://swift.org/package-manager/ 45 | 46 | Add this project as a dependency in your `Package.swift` file: 47 | 48 | ```swift 49 | dependencies: [ 50 | .package(url: "https://github.com/square/StringTemplate.git", .from("1.0.0")), 51 | ] 52 | ``` 53 | 54 | ### [Carthage] 55 | 56 | [Carthage]: https://github.com/Carthage/Carthage 57 | 58 | Add the following to your Cartfile: 59 | 60 | ``` 61 | github "square/StringTemplate" 62 | ``` 63 | 64 | Then run `carthage update`. 65 | 66 | Follow the current instructions in [Carthage's README][carthage-installation] 67 | for up to date installation instructions. 68 | 69 | [carthage-installation]: https://github.com/Carthage/Carthage#adding-frameworks-to-an-application 70 | 71 | ### [CocoaPods] 72 | 73 | [CocoaPods]: http://cocoapods.org 74 | 75 | Add the following to your [Podfile](http://guides.cocoapods.org/using/the-podfile.html): 76 | 77 | ```ruby 78 | pod 'StringTemplate' 79 | ``` 80 | 81 | You will also need to make sure you're opting into using frameworks: 82 | 83 | ```ruby 84 | use_frameworks! 85 | ``` 86 | 87 | Then run `pod install` with CocoaPods 0.36 or newer. 88 | 89 | ### Git Submodules 90 | 91 | I guess you could do it this way if that's your thing. 92 | 93 | Add this repo as a submodule, and add the project file to your workspace. You 94 | can then link against `StringTemplate.framework` for your application target. 95 | 96 | [Runes]: https://github.com/thoughtbot/Runes 97 | 98 | ## Contributing 99 | 100 | We love our 101 | [contributors](https://github.com/square/StringTemplate/graphs/contributors)! 102 | Please read our [contributing guidelines](Contributing.md) prior to submitting 103 | a pull request. 104 | 105 | License 106 | ------- 107 | 108 | Copyright 2017 Square, Inc. 109 | 110 | Licensed under the Apache License, Version 2.0 (the "License"); 111 | you may not use this file except in compliance with the License. 112 | You may obtain a copy of the License at 113 | 114 | http://www.apache.org/licenses/LICENSE-2.0 115 | 116 | Unless required by applicable law or agreed to in writing, software 117 | distributed under the License is distributed on an "AS IS" BASIS, 118 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 119 | See the License for the specific language governing permissions and 120 | limitations under the License. 121 | -------------------------------------------------------------------------------- /Tests/StringTemplateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2018 Square, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import StringTemplate 18 | import XCTest 19 | 20 | final class StringTemplateTests: XCTestCase { 21 | func testSuccessfulTemplateApplication() { 22 | let phone = String.Template(placeholderToken: "&", template: "(&&&) &&&-&&&&") 23 | let template = String.Template(placeholderToken: "%", template: "%% %%% (%%)*(%%)") 24 | 25 | XCTAssertEqual(try? "5555555555".applying(template: phone), "(555) 555-5555") 26 | XCTAssertEqual(try? "1234567890".applying(template: phone), "(123) 456-7890") 27 | XCTAssertEqual(try? "123456789".applying(template: template), "12 345 (67)*(89)") 28 | XCTAssertEqual(try? "000000000".applying(template: template), "00 000 (00)*(00)") 29 | } 30 | 31 | func testSuccessfulPartialTemplateApplication() { 32 | let phone = String.Template(placeholderToken: "&", template: "(&&&) &&&-&&&&") 33 | let template = String.Template(placeholderToken: "%", template: "%% %%% (%%)*(%%)") 34 | 35 | XCTAssertEqual(try? "5555555555".applying(template: phone, options: [.allowPartial]), "(555) 555-5555") 36 | XCTAssertEqual(try? "5555555".applying(template: phone, options: [.allowPartial]), "(555) 555-5") 37 | XCTAssertEqual(try? "123456".applying(template: phone, options: [.allowPartial]), "(123) 456-") 38 | XCTAssertEqual(try? "12345".applying(template: template, options: [.allowPartial]), "12 345 (") 39 | XCTAssertEqual(try? "0000000".applying(template: template, options: [.allowPartial]), "00 000 (00)*(") 40 | } 41 | 42 | func testSuccessfulOverflowTemplateApplication() { 43 | let phone = String.Template(placeholderToken: "&", template: "(&&&) &&&-&&&&") 44 | let template = String.Template(placeholderToken: "%", template: "%% %%% (%%)*(%%)") 45 | 46 | XCTAssertEqual(try? "5555555555555".applying(template: phone, options: [.allowOverflow]), "(555) 555-5555") 47 | XCTAssertEqual(try? "123456789000".applying(template: phone, options: [.allowOverflow]), "(123) 456-7890") 48 | XCTAssertEqual(try? "1234567890000".applying(template: template, options: [.allowOverflow]), "12 345 (67)*(89)") 49 | XCTAssertEqual(try? "00000000000000".applying(template: template, options: [.allowOverflow]), "00 000 (00)*(00)") 50 | } 51 | 52 | func testUnsuccessfulTemplateApplication() { 53 | let template = String.Template(placeholderToken: "X", template: "XXXX") 54 | 55 | assert(try "123".applying(template: template), throws: String.Template.Error.tooShort) 56 | assert(try "12345".applying(template: template), throws: String.Template.Error.tooLong) 57 | } 58 | 59 | func testUnsuccessfulPartialTemplateApplication() { 60 | let template = String.Template(placeholderToken: "X", template: "XXXX") 61 | 62 | XCTAssertEqual(try? "123".applying(template: template, options: [.allowPartial]), "123") 63 | assert(try "12345".applying(template: template, options: [.allowPartial]), throws: String.Template.Error.tooLong) 64 | } 65 | 66 | func testUnsuccessfulOverflowTemplateApplication() { 67 | let template = String.Template(placeholderToken: "X", template: "XXXX") 68 | 69 | XCTAssertEqual(try? "12345".applying(template: template, options: [.allowOverflow]), "1234") 70 | assert(try "123".applying(template: template, options: [.allowOverflow]), throws: String.Template.Error.tooShort) 71 | } 72 | 73 | func testPreservedCharacters() { 74 | let template = String.Template(placeholderToken: "X", template: "0XXX0", preservedCharacters: .decimalDigits) 75 | 76 | XCTAssertEqual(try? "103".applying(template: template), "01030") 77 | } 78 | } 79 | 80 | // Assert that a throwing function throws a specific error 81 | func assert(_ function: @autoclosure () throws -> T, throws expected: E, file: StaticString = #file, line: UInt = #line) where E: Error & Equatable { 82 | do { 83 | _ = try function() 84 | XCTFail("Expected an error to be thrown", file: file, line: line) 85 | } catch let error as E { 86 | XCTAssertEqual(error, expected, file: file, line: line) 87 | } catch { 88 | XCTFail("Incorrect error type was thrown: \(error)", file: file, line: line) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/String+Template.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2018 Square, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | import Foundation 18 | 19 | // MARK: - 20 | 21 | extension String { 22 | 23 | // MARK: - 24 | 25 | /** 26 | An opaque reference to a template that can be applied to a `String` instance. 27 | 28 | The internals for this type are intentionally hidden, and shouldn't be a 29 | concern of the caller. You should instead use `String.apply(template:)` (or 30 | one of the variants) in order to use instances of this type. 31 | */ 32 | public struct Template { 33 | 34 | // MARK: - 35 | 36 | public enum Error: Swift.Error { 37 | case tooLong 38 | case tooShort 39 | } 40 | 41 | // MARK: - 42 | 43 | /** 44 | Options for how to apply the template 45 | */ 46 | public struct Options: OptionSet { 47 | public let rawValue: Int 48 | 49 | public init(rawValue: Int) { 50 | self.rawValue = rawValue 51 | } 52 | 53 | /** 54 | Allow partial application. If this is set, the template will only 55 | be applied for the available characters in the target string, and 56 | the remaining template placeholder characters will be dropped. Any 57 | formatting characters that extend beyond the length of the target 58 | string will still be added to the result. 59 | 60 | ```swift 61 | let template = String.Template(placeholderToken: "X", template: "XXX-XXX") 62 | let string = "123".applying(template: template, options: [.allowPartial]) 63 | // string == "123-" 64 | ``` 65 | */ 66 | public static let allowPartial = Options(rawValue: 1) 67 | 68 | /** 69 | Allow overflow application. If this is set, any characters in the 70 | target string that extend beyond the template will be discarded. 71 | 72 | ```swift 73 | let template = String.Template(placeholderToken: "X", template: "XXX-XXX") 74 | let string = "12345678".applying(template: template, options: [.allowOverflow]) 75 | // string == "123-456" 76 | ``` 77 | */ 78 | public static let allowOverflow = Options(rawValue: 2) 79 | } 80 | 81 | // MARK: - Public Properties 82 | 83 | public let formattingCharacterSet: CharacterSet 84 | public let placeholderToken: Character 85 | public let template: String 86 | 87 | // MARK: - Private Properties 88 | 89 | fileprivate let length: Int 90 | 91 | // MARK: - Life Cycle 92 | 93 | /** 94 | Construct a new `Template`. 95 | 96 | The template will be created from the provided `placeholderToken` and 97 | `template` string. The `placeholderToken` should correspond to the 98 | character used in the template to denote the characters that should be 99 | replaced. 100 | 101 | If you have formatting characters in your template string that might 102 | also appear in your target string, you can use the optional 103 | `preservedCharacters` parameter to mark those characters as needing to 104 | be preserved so that they aren't stripped from the target during 105 | template application. 106 | 107 | For example: 108 | 109 | ```swift 110 | let template = String.Template(placeholderToken: "X", template: "0XX0") 111 | let string = try? "00".applying(template: template) 112 | // string == nil, because the character `0` is treated as a formatting 113 | // character, and so was stripped from the target string during 114 | // application. 115 | 116 | let fixedTemplate = String.Template(placeholderToken: "X", template: "0XX0", preservedCharacters: .decimalDigits) 117 | let fixedString = try? "00".applying(template: fixedTemplate) 118 | // string == "0000", because we explicitly marked decimal digits as 119 | // being preserved characters that should not be stripped from the 120 | // source. 121 | ``` 122 | 123 | - parameter placeholderToken: The token used in the template string to 124 | indicate where characters should be replaced. 125 | - parameter template: The template string to use. This string should 126 | represent a specific format and should use the `placeholderToken` to mark 127 | where character replacements will happen. 128 | - parameter preservedCharacters: A character set representing 129 | characters that should be treated as formatting characters, but should 130 | _not_ be stripped from the target string. Defaults to `nil`. 131 | */ 132 | public init(placeholderToken: Character, template: String, preservedCharacters: CharacterSet? = nil) { 133 | self.placeholderToken = placeholderToken 134 | self.template = template 135 | 136 | let placeholderCharacters = CharacterSet(character: placeholderToken) 137 | let formatting = template.removingCharacters(in: placeholderCharacters) 138 | 139 | var formattingCharacterSet = CharacterSet(charactersIn: formatting) 140 | 141 | self.length = template.removingCharacters(in: formattingCharacterSet).count 142 | 143 | if let preservedCharacters = preservedCharacters { 144 | formattingCharacterSet.subtract(preservedCharacters) 145 | } 146 | 147 | self.formattingCharacterSet = formattingCharacterSet 148 | } 149 | } 150 | 151 | 152 | // MARK: - Public Methods 153 | 154 | /** 155 | Apply a template to `self` with the provided options. This method mutates 156 | the callee. 157 | 158 | You can use the `options` parameter to conditionally allow partial or 159 | overflow behavior for the template application. This property defaults to 160 | an empty set of options, which will enforce an exact match between the 161 | length of the template and the length of the target. 162 | 163 | See `Template.Options` for more information. 164 | 165 | - parameter template: The `Template` instance to apply to `self` 166 | - parameter options: The `Template.Options` to use when applying the 167 | template. This defaults to `[]`, which results in enforcing an exact match 168 | between the template length and the target string length. 169 | - throws: String.Template.Error if the template application fails, usually 170 | because of a length mismatch. 171 | */ 172 | public mutating func apply(template: Template, options: Template.Options = []) throws { 173 | removeCharacters(in: template.formattingCharacterSet) 174 | 175 | try validate(length: template.length, options: options) 176 | 177 | var formatted = template.template 178 | var currentIndex = startIndex; 179 | var formatStringIndex = formatted.firstIndex(of: template.placeholderToken) 180 | 181 | while let placeholderIndex = formatStringIndex, currentIndex < endIndex { 182 | let characterToInsert = String(self[currentIndex]) 183 | formatted = formatted.replacingCharacters(in: placeholderIndex...placeholderIndex, with: characterToInsert) 184 | 185 | // Increment our indices 186 | currentIndex = index(currentIndex, offsetBy: 1) 187 | formatStringIndex = formatted[placeholderIndex...].firstIndex(of: template.placeholderToken) 188 | } 189 | 190 | // Only use the part of the string we formatted, defaulting to the whole string if we completed formatting 191 | let end = formatStringIndex ?? formatted.endIndex 192 | self = String(formatted[.. String { 216 | var copy = self 217 | try copy.apply(template: template, options: options) 218 | return copy 219 | } 220 | 221 | // MARK: - Private Methods 222 | 223 | private func validate(length: Int, options: Template.Options) throws { 224 | if !options.contains(.allowOverflow) { 225 | try validateMaxLength(length) 226 | } 227 | 228 | if !options.contains(.allowPartial) { 229 | try validateMinLength(length) 230 | } 231 | } 232 | 233 | private func validateMinLength(_ length: Int) throws { 234 | if count < length { 235 | throw String.Template.Error.tooShort 236 | } 237 | } 238 | 239 | private func validateMaxLength(_ length: Int) throws { 240 | if count > length { 241 | throw String.Template.Error.tooLong 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /StringTemplate.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "StringTemplate::StringTemplatePackageTests::ProductTarget" /* StringTemplatePackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_32 /* Build configuration list for PBXAggregateTarget "StringTemplatePackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_35 /* PBXTargetDependency */, 17 | ); 18 | name = StringTemplatePackageTests; 19 | productName = StringTemplatePackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | OBJ_21 /* CharacterSet+Character.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* CharacterSet+Character.swift */; }; 25 | OBJ_22 /* String+CharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* String+CharacterSet.swift */; }; 26 | OBJ_23 /* String+Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* String+Template.swift */; }; 27 | OBJ_30 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 28 | OBJ_41 /* StringTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* StringTemplateTests.swift */; }; 29 | OBJ_43 /* StringTemplate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "StringTemplate::StringTemplate::Product" /* StringTemplate.framework */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | 38FC52B120B734A300CBFDC5 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = OBJ_1 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = "StringTemplate::StringTemplate"; 38 | remoteInfo = StringTemplate; 39 | }; 40 | 38FC52B220B734A300CBFDC5 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = OBJ_1 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = "StringTemplate::StringTemplateTests"; 45 | remoteInfo = StringTemplateTests; 46 | }; 47 | /* End PBXContainerItemProxy section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | OBJ_10 /* String+Template.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Template.swift"; sourceTree = ""; }; 51 | OBJ_12 /* StringTemplateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTemplateTests.swift; sourceTree = ""; }; 52 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 53 | OBJ_8 /* CharacterSet+Character.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Character.swift"; sourceTree = ""; }; 54 | OBJ_9 /* String+CharacterSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+CharacterSet.swift"; sourceTree = ""; }; 55 | "StringTemplate::StringTemplate::Product" /* StringTemplate.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = StringTemplate.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | "StringTemplate::StringTemplateTests::Product" /* StringTemplateTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = StringTemplateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | OBJ_24 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 0; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | OBJ_42 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 0; 70 | files = ( 71 | OBJ_43 /* StringTemplate.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | OBJ_11 /* Tests */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | OBJ_12 /* StringTemplateTests.swift */, 82 | ); 83 | path = Tests; 84 | sourceTree = SOURCE_ROOT; 85 | }; 86 | OBJ_13 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | "StringTemplate::StringTemplateTests::Product" /* StringTemplateTests.xctest */, 90 | "StringTemplate::StringTemplate::Product" /* StringTemplate.framework */, 91 | ); 92 | name = Products; 93 | sourceTree = BUILT_PRODUCTS_DIR; 94 | }; 95 | OBJ_5 = { 96 | isa = PBXGroup; 97 | children = ( 98 | OBJ_6 /* Package.swift */, 99 | OBJ_7 /* Sources */, 100 | OBJ_11 /* Tests */, 101 | OBJ_13 /* Products */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | OBJ_7 /* Sources */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | OBJ_8 /* CharacterSet+Character.swift */, 109 | OBJ_9 /* String+CharacterSet.swift */, 110 | OBJ_10 /* String+Template.swift */, 111 | ); 112 | path = Sources; 113 | sourceTree = SOURCE_ROOT; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | "StringTemplate::StringTemplate" /* StringTemplate */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = OBJ_17 /* Build configuration list for PBXNativeTarget "StringTemplate" */; 121 | buildPhases = ( 122 | OBJ_20 /* Sources */, 123 | OBJ_24 /* Frameworks */, 124 | ); 125 | buildRules = ( 126 | ); 127 | dependencies = ( 128 | ); 129 | name = StringTemplate; 130 | productName = StringTemplate; 131 | productReference = "StringTemplate::StringTemplate::Product" /* StringTemplate.framework */; 132 | productType = "com.apple.product-type.framework"; 133 | }; 134 | "StringTemplate::StringTemplateTests" /* StringTemplateTests */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = OBJ_37 /* Build configuration list for PBXNativeTarget "StringTemplateTests" */; 137 | buildPhases = ( 138 | OBJ_40 /* Sources */, 139 | OBJ_42 /* Frameworks */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | OBJ_44 /* PBXTargetDependency */, 145 | ); 146 | name = StringTemplateTests; 147 | productName = StringTemplateTests; 148 | productReference = "StringTemplate::StringTemplateTests::Product" /* StringTemplateTests.xctest */; 149 | productType = "com.apple.product-type.bundle.unit-test"; 150 | }; 151 | "StringTemplate::SwiftPMPackageDescription" /* StringTemplatePackageDescription */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = OBJ_26 /* Build configuration list for PBXNativeTarget "StringTemplatePackageDescription" */; 154 | buildPhases = ( 155 | OBJ_29 /* Sources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = StringTemplatePackageDescription; 162 | productName = StringTemplatePackageDescription; 163 | productType = "com.apple.product-type.framework"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | OBJ_1 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastUpgradeCheck = 9999; 172 | }; 173 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "StringTemplate" */; 174 | compatibilityVersion = "Xcode 3.2"; 175 | developmentRegion = English; 176 | hasScannedForEncodings = 0; 177 | knownRegions = ( 178 | English, 179 | en, 180 | ); 181 | mainGroup = OBJ_5; 182 | productRefGroup = OBJ_13 /* Products */; 183 | projectDirPath = ""; 184 | projectRoot = ""; 185 | targets = ( 186 | "StringTemplate::StringTemplate" /* StringTemplate */, 187 | "StringTemplate::SwiftPMPackageDescription" /* StringTemplatePackageDescription */, 188 | "StringTemplate::StringTemplatePackageTests::ProductTarget" /* StringTemplatePackageTests */, 189 | "StringTemplate::StringTemplateTests" /* StringTemplateTests */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXSourcesBuildPhase section */ 195 | OBJ_20 /* Sources */ = { 196 | isa = PBXSourcesBuildPhase; 197 | buildActionMask = 0; 198 | files = ( 199 | OBJ_21 /* CharacterSet+Character.swift in Sources */, 200 | OBJ_22 /* String+CharacterSet.swift in Sources */, 201 | OBJ_23 /* String+Template.swift in Sources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | OBJ_29 /* Sources */ = { 206 | isa = PBXSourcesBuildPhase; 207 | buildActionMask = 0; 208 | files = ( 209 | OBJ_30 /* Package.swift in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | OBJ_40 /* Sources */ = { 214 | isa = PBXSourcesBuildPhase; 215 | buildActionMask = 0; 216 | files = ( 217 | OBJ_41 /* StringTemplateTests.swift in Sources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXSourcesBuildPhase section */ 222 | 223 | /* Begin PBXTargetDependency section */ 224 | OBJ_35 /* PBXTargetDependency */ = { 225 | isa = PBXTargetDependency; 226 | target = "StringTemplate::StringTemplateTests" /* StringTemplateTests */; 227 | targetProxy = 38FC52B220B734A300CBFDC5 /* PBXContainerItemProxy */; 228 | }; 229 | OBJ_44 /* PBXTargetDependency */ = { 230 | isa = PBXTargetDependency; 231 | target = "StringTemplate::StringTemplate" /* StringTemplate */; 232 | targetProxy = 38FC52B120B734A300CBFDC5 /* PBXContainerItemProxy */; 233 | }; 234 | /* End PBXTargetDependency section */ 235 | 236 | /* Begin XCBuildConfiguration section */ 237 | OBJ_18 /* Debug */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ENABLE_TESTABILITY = YES; 241 | FRAMEWORK_SEARCH_PATHS = ( 242 | "$(inherited)", 243 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 244 | ); 245 | HEADER_SEARCH_PATHS = "$(inherited)"; 246 | INFOPLIST_FILE = StringTemplate.xcodeproj/StringTemplate_Info.plist; 247 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 248 | OTHER_CFLAGS = "$(inherited)"; 249 | OTHER_LDFLAGS = "$(inherited)"; 250 | OTHER_SWIFT_FLAGS = "$(inherited)"; 251 | PRODUCT_BUNDLE_IDENTIFIER = StringTemplate; 252 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 253 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 254 | SKIP_INSTALL = YES; 255 | SWIFT_VERSION = 4.0; 256 | TARGET_NAME = StringTemplate; 257 | }; 258 | name = Debug; 259 | }; 260 | OBJ_19 /* Release */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ENABLE_TESTABILITY = YES; 264 | FRAMEWORK_SEARCH_PATHS = ( 265 | "$(inherited)", 266 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 267 | ); 268 | HEADER_SEARCH_PATHS = "$(inherited)"; 269 | INFOPLIST_FILE = StringTemplate.xcodeproj/StringTemplate_Info.plist; 270 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 271 | OTHER_CFLAGS = "$(inherited)"; 272 | OTHER_LDFLAGS = "$(inherited)"; 273 | OTHER_SWIFT_FLAGS = "$(inherited)"; 274 | PRODUCT_BUNDLE_IDENTIFIER = StringTemplate; 275 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 276 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 277 | SKIP_INSTALL = YES; 278 | SWIFT_VERSION = 4.0; 279 | TARGET_NAME = StringTemplate; 280 | }; 281 | name = Release; 282 | }; 283 | OBJ_27 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | LD = /usr/bin/true; 287 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-9.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 288 | SWIFT_VERSION = 4.0; 289 | }; 290 | name = Debug; 291 | }; 292 | OBJ_28 /* Release */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | LD = /usr/bin/true; 296 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-9.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 297 | SWIFT_VERSION = 4.0; 298 | }; 299 | name = Release; 300 | }; 301 | OBJ_3 /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | CLANG_ENABLE_OBJC_ARC = YES; 305 | COMBINE_HIDPI_IMAGES = YES; 306 | COPY_PHASE_STRIP = NO; 307 | DEBUG_INFORMATION_FORMAT = dwarf; 308 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 309 | ENABLE_NS_ASSERTIONS = YES; 310 | GCC_OPTIMIZATION_LEVEL = 0; 311 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 312 | MACOSX_DEPLOYMENT_TARGET = 10.13; 313 | ONLY_ACTIVE_ARCH = YES; 314 | OTHER_SWIFT_FLAGS = "-DXcode"; 315 | PRODUCT_NAME = "$(TARGET_NAME)"; 316 | SDKROOT = macosx; 317 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 318 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 319 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 320 | TVOS_DEPLOYMENT_TARGET = 11.0; 321 | USE_HEADERMAP = NO; 322 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 323 | }; 324 | name = Debug; 325 | }; 326 | OBJ_33 /* Debug */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | }; 330 | name = Debug; 331 | }; 332 | OBJ_34 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | }; 336 | name = Release; 337 | }; 338 | OBJ_38 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 342 | FRAMEWORK_SEARCH_PATHS = ( 343 | "$(inherited)", 344 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 345 | ); 346 | HEADER_SEARCH_PATHS = "$(inherited)"; 347 | INFOPLIST_FILE = StringTemplate.xcodeproj/StringTemplateTests_Info.plist; 348 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 349 | OTHER_CFLAGS = "$(inherited)"; 350 | OTHER_LDFLAGS = "$(inherited)"; 351 | OTHER_SWIFT_FLAGS = "$(inherited)"; 352 | SWIFT_VERSION = 4.0; 353 | TARGET_NAME = StringTemplateTests; 354 | }; 355 | name = Debug; 356 | }; 357 | OBJ_39 /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 361 | FRAMEWORK_SEARCH_PATHS = ( 362 | "$(inherited)", 363 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 364 | ); 365 | HEADER_SEARCH_PATHS = "$(inherited)"; 366 | INFOPLIST_FILE = StringTemplate.xcodeproj/StringTemplateTests_Info.plist; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 368 | OTHER_CFLAGS = "$(inherited)"; 369 | OTHER_LDFLAGS = "$(inherited)"; 370 | OTHER_SWIFT_FLAGS = "$(inherited)"; 371 | SWIFT_VERSION = 4.0; 372 | TARGET_NAME = StringTemplateTests; 373 | }; 374 | name = Release; 375 | }; 376 | OBJ_4 /* Release */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | CLANG_ENABLE_OBJC_ARC = YES; 380 | COMBINE_HIDPI_IMAGES = YES; 381 | COPY_PHASE_STRIP = YES; 382 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 383 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 384 | GCC_OPTIMIZATION_LEVEL = s; 385 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 386 | MACOSX_DEPLOYMENT_TARGET = 10.13; 387 | OTHER_SWIFT_FLAGS = "-DXcode"; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SDKROOT = macosx; 390 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 391 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 392 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 393 | TVOS_DEPLOYMENT_TARGET = 11.0; 394 | USE_HEADERMAP = NO; 395 | WATCHOS_DEPLOYMENT_TARGET = 4.0; 396 | }; 397 | name = Release; 398 | }; 399 | /* End XCBuildConfiguration section */ 400 | 401 | /* Begin XCConfigurationList section */ 402 | OBJ_17 /* Build configuration list for PBXNativeTarget "StringTemplate" */ = { 403 | isa = XCConfigurationList; 404 | buildConfigurations = ( 405 | OBJ_18 /* Debug */, 406 | OBJ_19 /* Release */, 407 | ); 408 | defaultConfigurationIsVisible = 0; 409 | defaultConfigurationName = Release; 410 | }; 411 | OBJ_2 /* Build configuration list for PBXProject "StringTemplate" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | OBJ_3 /* Debug */, 415 | OBJ_4 /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | OBJ_26 /* Build configuration list for PBXNativeTarget "StringTemplatePackageDescription" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | OBJ_27 /* Debug */, 424 | OBJ_28 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | OBJ_32 /* Build configuration list for PBXAggregateTarget "StringTemplatePackageTests" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | OBJ_33 /* Debug */, 433 | OBJ_34 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | OBJ_37 /* Build configuration list for PBXNativeTarget "StringTemplateTests" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | OBJ_38 /* Debug */, 442 | OBJ_39 /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | /* End XCConfigurationList section */ 448 | }; 449 | rootObject = OBJ_1 /* Project object */; 450 | } 451 | --------------------------------------------------------------------------------