├── .gitignore
├── Formatting.playground
├── Contents.swift
└── contents.xcplayground
├── Formatting.xcodeproj
├── Configs
│ └── Project.xcconfig
├── FormattingTestSuite_Info.plist
├── Formatting_Info.plist
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── Formatting.xcscheme
│ └── xcschememanagement.plist
├── LICENSE.txt
├── Package.swift
├── README.md
├── Sources
├── Date.swift
├── Formatter.swift
├── Formatters.swift
├── Operators.swift
└── Uncurry.swift
└── Tests
├── Formatting
└── FormattingTests.swift
└── LinuxMain.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | *.moved-aside
17 | DerivedData
18 | *.hmap
19 | *.ipa
20 | *.xcuserstate
21 |
--------------------------------------------------------------------------------
/Formatting.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import Formatting
2 | import Foundation
3 |
4 |
5 |
6 | format("Hello, " % string % "! It's " % K % " o'clock.", "world", Date())
7 |
8 |
9 |
10 | let dateFormatter =
11 | format(yyyy % "-" <> MM % "-" <> dd)
12 |
13 | dateFormatter(Date())
14 |
15 |
16 |
17 | let longDateFormatter =
18 | format(date(.long))
19 |
20 | longDateFormatter(Date())
21 |
22 |
23 |
24 | format(bytes)(1024)
25 |
26 |
27 |
28 | let logFormatter =
29 | format(fitLeft(5) .% right(5) % " -- [" % iso8601 % "] " % string)
30 |
31 | let infoLog = logFormatter("INFO")
32 | let debugLog = logFormatter("DEBUG")
33 |
34 | print(infoLog(Date())("Logging in..."))
35 | print(debugLog(Date())("Logged in successfully!"))
36 |
--------------------------------------------------------------------------------
/Formatting.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/Configs/Project.xcconfig:
--------------------------------------------------------------------------------
1 | PRODUCT_NAME = $(TARGET_NAME)
2 | SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator
3 | MACOSX_DEPLOYMENT_TARGET = 10.10
4 | DYLIB_INSTALL_NAME_BASE = @rpath
5 | OTHER_SWIFT_FLAGS = -DXcode
6 | COMBINE_HIDPI_IMAGES = YES
7 | USE_HEADERMAP = NO
8 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/FormattingTestSuite_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 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/Formatting_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 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 80F0A63E1D206A1900FFB48A /* Uncurry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F0A63D1D206A1900FFB48A /* Uncurry.swift */; };
11 | _LinkFileRef_Formatting_via_FormattingTestSuite /* Formatting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "_____Product_Formatting" /* Formatting.framework */; };
12 | __src_cc_ref_Sources/Date.swift /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Sources/Date.swift /* Date.swift */; };
13 | __src_cc_ref_Sources/Formatter.swift /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Sources/Formatter.swift /* Formatter.swift */; };
14 | __src_cc_ref_Sources/Formatters.swift /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Sources/Formatters.swift /* Formatters.swift */; };
15 | __src_cc_ref_Sources/Operators.swift /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Sources/Operators.swift /* Operators.swift */; };
16 | __src_cc_ref_Tests/Formatting/FormattingTests.swift /* FormattingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Tests/Formatting/FormattingTests.swift /* FormattingTests.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 80F0A63B1D2040E400FFB48A /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = __RootObject_ /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = "______Target_Formatting";
25 | remoteInfo = Formatting;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXFileReference section */
30 | 80F0A63C1D2040F100FFB48A /* Formatting.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Formatting.playground; sourceTree = ""; };
31 | 80F0A63D1D206A1900FFB48A /* Uncurry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Uncurry.swift; sourceTree = ""; };
32 | 80F5CAA81D2553CB00625455 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
33 | 80F5CAAA1D2553D100625455 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; };
34 | __PBXFileRef_Formatting.xcodeproj/Configs/Project.xcconfig /* Project.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Project.xcconfig; path = Formatting.xcodeproj/Configs/Project.xcconfig; sourceTree = ""; };
35 | __PBXFileRef_FormattingTestSuite_Info.plist /* FormattingTestSuite_Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = FormattingTestSuite_Info.plist; path = Formatting.xcodeproj/FormattingTestSuite_Info.plist; sourceTree = SOURCE_ROOT; };
36 | __PBXFileRef_Formatting_Info.plist /* Formatting_Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Formatting_Info.plist; path = Formatting.xcodeproj/Formatting_Info.plist; sourceTree = SOURCE_ROOT; };
37 | __PBXFileRef_Package.swift /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
38 | __PBXFileRef_Sources/Date.swift /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
39 | __PBXFileRef_Sources/Formatter.swift /* Formatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; };
40 | __PBXFileRef_Sources/Formatters.swift /* Formatters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatters.swift; sourceTree = ""; };
41 | __PBXFileRef_Sources/Operators.swift /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; };
42 | __PBXFileRef_Tests/Formatting/FormattingTests.swift /* FormattingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattingTests.swift; sourceTree = ""; };
43 | "_____Product_Formatting" /* Formatting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Formatting.framework; sourceTree = BUILT_PRODUCTS_DIR; };
44 | "_____Product_FormattingTestSuite" /* FormattingTestSuite.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = FormattingTestSuite.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | "___LinkPhase_Formatting" /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 0;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | "___LinkPhase_FormattingTestSuite" /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 0;
58 | files = (
59 | _LinkFileRef_Formatting_via_FormattingTestSuite /* Formatting.framework in Frameworks */,
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | /* End PBXFrameworksBuildPhase section */
64 |
65 | /* Begin PBXGroup section */
66 | TestProducts_ /* Tests */ = {
67 | isa = PBXGroup;
68 | children = (
69 | "_____Product_FormattingTestSuite" /* FormattingTestSuite.xctest */,
70 | );
71 | name = Tests;
72 | sourceTree = "";
73 | };
74 | "___RootGroup_" = {
75 | isa = PBXGroup;
76 | children = (
77 | 80F5CAA81D2553CB00625455 /* README.md */,
78 | 80F5CAAA1D2553D100625455 /* LICENSE.txt */,
79 | 80F0A63C1D2040F100FFB48A /* Formatting.playground */,
80 | __PBXFileRef_Package.swift /* Package.swift */,
81 | "_____Configs_" /* Configs */,
82 | "_____Sources_" /* Sources */,
83 | "_______Tests_" /* Tests */,
84 | "____Products_" /* Products */,
85 | );
86 | sourceTree = "";
87 | };
88 | "____Products_" /* Products */ = {
89 | isa = PBXGroup;
90 | children = (
91 | TestProducts_ /* Tests */,
92 | "_____Product_Formatting" /* Formatting.framework */,
93 | );
94 | name = Products;
95 | sourceTree = "";
96 | };
97 | "_____Configs_" /* Configs */ = {
98 | isa = PBXGroup;
99 | children = (
100 | __PBXFileRef_Formatting.xcodeproj/Configs/Project.xcconfig /* Project.xcconfig */,
101 | );
102 | name = Configs;
103 | sourceTree = "";
104 | };
105 | "_____Sources_" /* Sources */ = {
106 | isa = PBXGroup;
107 | children = (
108 | "_______Group_Formatting" /* Formatting */,
109 | );
110 | name = Sources;
111 | sourceTree = "";
112 | };
113 | "_______Group_Formatting" /* Formatting */ = {
114 | isa = PBXGroup;
115 | children = (
116 | __PBXFileRef_Sources/Date.swift /* Date.swift */,
117 | __PBXFileRef_Sources/Formatter.swift /* Formatter.swift */,
118 | __PBXFileRef_Sources/Formatters.swift /* Formatters.swift */,
119 | __PBXFileRef_Sources/Operators.swift /* Operators.swift */,
120 | 80F0A63D1D206A1900FFB48A /* Uncurry.swift */,
121 | __PBXFileRef_Formatting_Info.plist /* Formatting_Info.plist */,
122 | );
123 | name = Formatting;
124 | path = Sources;
125 | sourceTree = "";
126 | };
127 | "_______Group_FormattingTestSuite" /* FormattingTestSuite */ = {
128 | isa = PBXGroup;
129 | children = (
130 | __PBXFileRef_Tests/Formatting/FormattingTests.swift /* FormattingTests.swift */,
131 | __PBXFileRef_FormattingTestSuite_Info.plist /* FormattingTestSuite_Info.plist */,
132 | );
133 | name = FormattingTestSuite;
134 | path = Tests/Formatting;
135 | sourceTree = "";
136 | };
137 | "_______Tests_" /* Tests */ = {
138 | isa = PBXGroup;
139 | children = (
140 | "_______Group_FormattingTestSuite" /* FormattingTestSuite */,
141 | );
142 | name = Tests;
143 | sourceTree = "";
144 | };
145 | /* End PBXGroup section */
146 |
147 | /* Begin PBXNativeTarget section */
148 | "______Target_Formatting" /* Formatting */ = {
149 | isa = PBXNativeTarget;
150 | buildConfigurationList = "_______Confs_Formatting" /* Build configuration list for PBXNativeTarget "Formatting" */;
151 | buildPhases = (
152 | CompilePhase_Formatting /* Sources */,
153 | "___LinkPhase_Formatting" /* Frameworks */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | );
159 | name = Formatting;
160 | productName = Formatting;
161 | productReference = "_____Product_Formatting" /* Formatting.framework */;
162 | productType = "com.apple.product-type.framework";
163 | };
164 | "______Target_FormattingTestSuite" /* FormattingTestSuite */ = {
165 | isa = PBXNativeTarget;
166 | buildConfigurationList = "_______Confs_FormattingTestSuite" /* Build configuration list for PBXNativeTarget "FormattingTestSuite" */;
167 | buildPhases = (
168 | CompilePhase_FormattingTestSuite /* Sources */,
169 | "___LinkPhase_FormattingTestSuite" /* Frameworks */,
170 | );
171 | buildRules = (
172 | );
173 | dependencies = (
174 | __Dependency_Formatting /* PBXTargetDependency */,
175 | );
176 | name = FormattingTestSuite;
177 | productName = FormattingTestSuite;
178 | productReference = "_____Product_FormattingTestSuite" /* FormattingTestSuite.xctest */;
179 | productType = "com.apple.product-type.bundle.unit-test";
180 | };
181 | /* End PBXNativeTarget section */
182 |
183 | /* Begin PBXProject section */
184 | __RootObject_ /* Project object */ = {
185 | isa = PBXProject;
186 | attributes = {
187 | LastUpgradeCheck = 9999;
188 | };
189 | buildConfigurationList = "___RootConfs_" /* Build configuration list for PBXProject "Formatting" */;
190 | compatibilityVersion = "Xcode 3.2";
191 | developmentRegion = English;
192 | hasScannedForEncodings = 0;
193 | knownRegions = (
194 | en,
195 | );
196 | mainGroup = "___RootGroup_";
197 | productRefGroup = "____Products_" /* Products */;
198 | projectDirPath = "";
199 | projectRoot = "";
200 | targets = (
201 | "______Target_Formatting" /* Formatting */,
202 | "______Target_FormattingTestSuite" /* FormattingTestSuite */,
203 | );
204 | };
205 | /* End PBXProject section */
206 |
207 | /* Begin PBXSourcesBuildPhase section */
208 | CompilePhase_Formatting /* Sources */ = {
209 | isa = PBXSourcesBuildPhase;
210 | buildActionMask = 0;
211 | files = (
212 | __src_cc_ref_Sources/Date.swift /* Date.swift in Sources */,
213 | __src_cc_ref_Sources/Formatter.swift /* Formatter.swift in Sources */,
214 | 80F0A63E1D206A1900FFB48A /* Uncurry.swift in Sources */,
215 | __src_cc_ref_Sources/Formatters.swift /* Formatters.swift in Sources */,
216 | __src_cc_ref_Sources/Operators.swift /* Operators.swift in Sources */,
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | CompilePhase_FormattingTestSuite /* Sources */ = {
221 | isa = PBXSourcesBuildPhase;
222 | buildActionMask = 0;
223 | files = (
224 | __src_cc_ref_Tests/Formatting/FormattingTests.swift /* FormattingTests.swift in Sources */,
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | /* End PBXSourcesBuildPhase section */
229 |
230 | /* Begin PBXTargetDependency section */
231 | __Dependency_Formatting /* PBXTargetDependency */ = {
232 | isa = PBXTargetDependency;
233 | target = "______Target_Formatting" /* Formatting */;
234 | targetProxy = 80F0A63B1D2040E400FFB48A /* PBXContainerItemProxy */;
235 | };
236 | /* End PBXTargetDependency section */
237 |
238 | /* Begin XCBuildConfiguration section */
239 | _ReleaseConf_Formatting /* Release */ = {
240 | isa = XCBuildConfiguration;
241 | buildSettings = {
242 | ENABLE_TESTABILITY = YES;
243 | FRAMEWORK_SEARCH_PATHS = "$(PLATFORM_DIR)/Developer/Library/Frameworks";
244 | INFOPLIST_FILE = Formatting.xcodeproj/Formatting_Info.plist;
245 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
246 | OTHER_LDFLAGS = "$(inherited)";
247 | OTHER_SWIFT_FLAGS = "$(inherited)";
248 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
249 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
250 | };
251 | name = Release;
252 | };
253 | _ReleaseConf_FormattingTestSuite /* Release */ = {
254 | isa = XCBuildConfiguration;
255 | buildSettings = {
256 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
257 | FRAMEWORK_SEARCH_PATHS = "$(PLATFORM_DIR)/Developer/Library/Frameworks";
258 | INFOPLIST_FILE = Formatting.xcodeproj/FormattingTestSuite_Info.plist;
259 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
260 | OTHER_LDFLAGS = "$(inherited)";
261 | OTHER_SWIFT_FLAGS = "$(inherited)";
262 | };
263 | name = Release;
264 | };
265 | "___DebugConf_Formatting" /* Debug */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ENABLE_TESTABILITY = YES;
269 | FRAMEWORK_SEARCH_PATHS = "$(PLATFORM_DIR)/Developer/Library/Frameworks";
270 | INFOPLIST_FILE = Formatting.xcodeproj/Formatting_Info.plist;
271 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
272 | OTHER_LDFLAGS = "$(inherited)";
273 | OTHER_SWIFT_FLAGS = "$(inherited)";
274 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
275 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
276 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
277 | };
278 | name = Debug;
279 | };
280 | "___DebugConf_FormattingTestSuite" /* Debug */ = {
281 | isa = XCBuildConfiguration;
282 | buildSettings = {
283 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
284 | FRAMEWORK_SEARCH_PATHS = "$(PLATFORM_DIR)/Developer/Library/Frameworks";
285 | INFOPLIST_FILE = Formatting.xcodeproj/FormattingTestSuite_Info.plist;
286 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
287 | OTHER_LDFLAGS = "$(inherited)";
288 | OTHER_SWIFT_FLAGS = "$(inherited)";
289 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
290 | };
291 | name = Debug;
292 | };
293 | "_____Release_" /* Release */ = {
294 | isa = XCBuildConfiguration;
295 | baseConfigurationReference = __PBXFileRef_Formatting.xcodeproj/Configs/Project.xcconfig /* Project.xcconfig */;
296 | buildSettings = {
297 | SWIFT_VERSION = 3.0;
298 | };
299 | name = Release;
300 | };
301 | "_______Debug_" /* Debug */ = {
302 | isa = XCBuildConfiguration;
303 | baseConfigurationReference = __PBXFileRef_Formatting.xcodeproj/Configs/Project.xcconfig /* Project.xcconfig */;
304 | buildSettings = {
305 | SWIFT_VERSION = 3.0;
306 | };
307 | name = Debug;
308 | };
309 | /* End XCBuildConfiguration section */
310 |
311 | /* Begin XCConfigurationList section */
312 | "___RootConfs_" /* Build configuration list for PBXProject "Formatting" */ = {
313 | isa = XCConfigurationList;
314 | buildConfigurations = (
315 | "_______Debug_" /* Debug */,
316 | "_____Release_" /* Release */,
317 | );
318 | defaultConfigurationIsVisible = 0;
319 | defaultConfigurationName = Debug;
320 | };
321 | "_______Confs_Formatting" /* Build configuration list for PBXNativeTarget "Formatting" */ = {
322 | isa = XCConfigurationList;
323 | buildConfigurations = (
324 | "___DebugConf_Formatting" /* Debug */,
325 | _ReleaseConf_Formatting /* Release */,
326 | );
327 | defaultConfigurationIsVisible = 0;
328 | defaultConfigurationName = Debug;
329 | };
330 | "_______Confs_FormattingTestSuite" /* Build configuration list for PBXNativeTarget "FormattingTestSuite" */ = {
331 | isa = XCConfigurationList;
332 | buildConfigurations = (
333 | "___DebugConf_FormattingTestSuite" /* Debug */,
334 | _ReleaseConf_FormattingTestSuite /* Release */,
335 | );
336 | defaultConfigurationIsVisible = 0;
337 | defaultConfigurationName = Debug;
338 | };
339 | /* End XCConfigurationList section */
340 | };
341 | rootObject = __RootObject_ /* Project object */;
342 | }
343 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/xcshareddata/xcschemes/Formatting.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Formatting.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SchemeUserState
5 |
6 | Formatting.xcscheme
7 |
8 |
9 | SuppressBuildableAutocreation
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2016 Stephen Celis ()
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | import PackageDescription
2 |
3 | let package = Package(
4 | name: "Formatting"
5 | )
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Formatting
2 |
3 | Type-safe, functional string formatting in Swift.
4 |
5 | Inspired by Chris Done's excellent [Haskell library](https://github.com/chrisdone/formatting).
6 |
7 | ``` swift
8 | import Formatting
9 |
10 | format("Hello, " % string % "!", "world")
11 | // "Hello, world!"
12 | ```
13 |
14 |
15 | ## Introduction
16 |
17 | Traditional string formatting methods (interpolation, `printf`, and template strings) can lead to subtle (and not so subtle) runtime bugs.
18 |
19 | ``` swift
20 | print("Hello, \(thing)!") // Hello, nil!
21 | print("Hello, \(thing)!") // Hello, Optional("world")!
22 | ```
23 |
24 | ``` objective-c
25 | NSLog(@"Hello, %@!", thing); // Hello, (null)!
26 | ```
27 |
28 | **Formatting** brings compile-time checks.
29 |
30 | ``` swift
31 | print("Hello, " % string % "!", thing) // Value of optional type String? not unwrapped
32 | ```
33 |
34 | And composability.
35 |
36 | ``` swift
37 | let greet =
38 | format("Hello, " % string % "!")
39 |
40 | greet("world") // Hello, world!
41 | ```
42 |
43 |
44 |
45 | ## Composing formatters
46 |
47 | Use `%` to build a formatter with strings and other formatters.
48 |
49 | ``` swift
50 | format(string % " is " % int % "years old.", "Alice", 25)
51 | // "Alice is 25 years old."
52 | ```
53 |
54 | Use `<>` to pass the previous formatter argument to the next formatter.
55 |
56 | ``` swift
57 | format(yyyy % "-" <> MM % "-" <> dd, Date())
58 | // "2016-06-28"
59 | ```
60 |
61 | Use `.%` to feed the result of one formatter into another.
62 |
63 | ``` swift
64 | format(left(2, "0") .% hex, 10)
65 | // "0a"
66 | ```
67 |
68 | Call `format` without arguments to return a curried formatter function.
69 |
70 | ``` swift
71 | let log =
72 | format(right(5) % " -- [" % iso8601 % "] " % string)
73 |
74 | let infoLog = log("INFO")
75 | let debugLog = log("DEBUG")
76 |
77 | infoLog(Date())("Logging in...")
78 | // "INFO -- [2016-06-28T12:34:56Z] Logging in..."
79 | debugLog(Date())("Logged in successfully!")
80 | // "DEBUG -- [2016-06-28T12:34:56Z] Logged in successfully!"
81 | ```
82 |
83 | ## License
84 |
85 | Formatting is available under the MIT license. See [the LICENSE
86 | file](./LICENSE.txt) for more information.
87 |
--------------------------------------------------------------------------------
/Sources/Date.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public func date(_ formatter: DateFormatter) -> FormatterOf {
4 | return Formatter { f in { date in f(formatter.string(from: date)) } }
5 | }
6 |
7 | private func date(builder: (DateFormatter) -> ()) -> FormatterOf {
8 | let df = DateFormatter()
9 | builder(df)
10 | return date(df)
11 | }
12 |
13 | public func date(format: String) -> FormatterOf {
14 | return date { $0.dateFormat = format }
15 | }
16 |
17 | public func date(_ dateStyle: DateFormatter.Style, time timeStyle: DateFormatter.Style = .none)
18 | -> FormatterOf {
19 | assert(dateStyle != .none, "use the `time()` formatter for time")
20 | return date { ($0.dateStyle, $0.timeStyle) = (dateStyle, timeStyle) }
21 | }
22 |
23 | public func time(_ timeStyle: DateFormatter.Style) -> FormatterOf {
24 | return date(.none, time: timeStyle)
25 | }
26 |
27 | /**
28 | An ISO8601 formatter.
29 |
30 | - parameter options: formatting options
31 | */
32 | @available(iOS 10, OSX 10.12, tvOS 10, watchOS 3, *)
33 | public func iso8601(_ options: ISO8601DateFormatter.Options = []) -> FormatterOf {
34 | let df = ISO8601DateFormatter()
35 | if !options.isEmpty {
36 | df.formatOptions = options
37 | }
38 | return Formatter { f in { date in f(df.string(from: date)) } }
39 | }
40 |
41 | /**
42 | An ISO8601 formatter with default `Foundation.ISO8601DateFormatter.Options`.
43 | */
44 | @available(iOS 10, OSX 10.12, tvOS 10, watchOS 3, *)
45 | public func iso8601() -> FormatterOf {
46 | return iso8601([])
47 | }
48 |
49 | // MARK: Era
50 |
51 | /**
52 | Era. Abbreviated form.
53 |
54 | format(G)(Date())
55 | // "AD"
56 | */
57 | public func G() -> FormatterOf {
58 | return date(format: "G")
59 | }
60 |
61 | /**
62 | Era. Long form.
63 |
64 | format(GGGG)(Date())
65 | // "Anno Domini"
66 | */
67 | public func GGGG() -> FormatterOf {
68 | return date(format: "GGGG")
69 | }
70 |
71 | /**
72 | Era. Narrow form.
73 |
74 | format(GGGGG)(Date())
75 | // "A"
76 | */
77 | public func GGGGG() -> FormatterOf {
78 | return date(format: "GGGGG")
79 | }
80 |
81 | // MARK: Year
82 |
83 | /**
84 | Year.
85 |
86 | format(y)(Date())
87 | // "2016"
88 | */
89 | public func y() -> FormatterOf {
90 | return date(format: "y")
91 | }
92 |
93 | /**
94 | Year. Zero-padded and truncated to 2 characters.
95 |
96 | format(yy)(ad1)
97 | // "01"
98 | format(yy)(Date())
99 | // "16"
100 | */
101 | public func yy() -> FormatterOf {
102 | return date(format: "yy")
103 | }
104 |
105 | /**
106 | Year. Zero-padded to 4 characters.
107 |
108 | format(yyyy)(ad1)
109 | // "0001"
110 | format(yyyy)(Date())
111 | // "2016"
112 | */
113 | public func yyyy() -> FormatterOf {
114 | return date(format: "yyyy")
115 | }
116 |
117 | /**
118 | Year (in "Week of Year" based calendars). May not always be the same value as calendar year.
119 | */
120 | public func Y() -> FormatterOf {
121 | return date(format: "Y")
122 | }
123 |
124 | /**
125 | Year (in "Week of Year" based calendars). May not always be the same value as calendar year. Padded and
126 | truncated to 2 characters.
127 | */
128 | public func YY() -> FormatterOf {
129 | return date(format: "YY")
130 | }
131 |
132 | /**
133 | Year (in "Week of Year" based calendars). May not always be the same value as calendar year. Zero-padded to
134 | 4 characters.
135 | */
136 | public func YYYY() -> FormatterOf {
137 | return date(format: "YYYY")
138 | }
139 |
140 | // MARK: Quarter
141 |
142 | /**
143 | Quarter. Numerical.
144 | */
145 | public func Q() -> FormatterOf {
146 | return date(format: "Q")
147 | }
148 |
149 | /**
150 | Quarter. Abbreviation.
151 | */
152 | public func QQQ() -> FormatterOf {
153 | return date(format: "QQQ")
154 | }
155 |
156 | /**
157 | Quarter. Full name.
158 | */
159 | public func QQQQ() -> FormatterOf {
160 | return date(format: "QQQQ")
161 | }
162 |
163 | /**
164 | **Stand-alone** quarter. Numerical.
165 | */
166 | public func q() -> FormatterOf {
167 | return date(format: "q")
168 | }
169 |
170 | /**
171 | **Stand-alone** quarter. Abbreviation.
172 | */
173 | public func qqq() -> FormatterOf {
174 | return date(format: "qqq")
175 | }
176 |
177 | /**
178 | **Stand-alone** quarter. Full name.
179 | */
180 | public func qqqq() -> FormatterOf {
181 | return date(format: "qqqq")
182 | }
183 |
184 | // MARK: Month
185 |
186 | /**
187 | Month. Numerical.
188 | */
189 | public func M() -> FormatterOf {
190 | return date(format: "M")
191 | }
192 |
193 | /**
194 | Month. Numerical. Zero-padded.
195 | */
196 | public func MM() -> FormatterOf {
197 | return date(format: "MM")
198 | }
199 |
200 | /**
201 | Month. Abbreviation.
202 | */
203 | public func MMM() -> FormatterOf {
204 | return date(format: "MMM")
205 | }
206 |
207 | /**
208 | Month. Full name.
209 | */
210 | public func MMMM() -> FormatterOf {
211 | return date(format: "MMMM")
212 | }
213 |
214 | /**
215 | Month. Narrow name.
216 | */
217 | public func MMMMM() -> FormatterOf {
218 | return date(format: "MMMMM")
219 | }
220 |
221 | // MARK: Week
222 |
223 | /**
224 | Week of year.
225 | */
226 | public func w() -> FormatterOf {
227 | return date(format: "w")
228 | }
229 |
230 | /**
231 | Week of year. Zero-padded.
232 | */
233 | public func ww() -> FormatterOf {
234 | return date(format: "ww")
235 | }
236 |
237 | /**
238 | Week of month.
239 | */
240 | public func W() -> FormatterOf {
241 | return date(format: "W")
242 | }
243 |
244 | // MARK: Day
245 |
246 | /**
247 | Date (day of the month).
248 | */
249 | public func d() -> FormatterOf {
250 | return date(format: "d")
251 | }
252 |
253 | /**
254 | Date (day of the month). Zero-padded.
255 | */
256 | public func dd() -> FormatterOf {
257 | return date(format: "dd")
258 | }
259 |
260 | /**
261 | Day of year.
262 | */
263 | public func D() -> FormatterOf {
264 | return date(format: "D")
265 | }
266 |
267 | /**
268 | Day of year. Zero-padded.
269 | */
270 | public func DDD() -> FormatterOf {
271 | return date(format: "DDD")
272 | }
273 |
274 | /**
275 | Day of week in month.
276 | */
277 | public func F() -> FormatterOf {
278 | return date(format: "F")
279 | }
280 |
281 | // MARK: Week day
282 |
283 | /**
284 | Day of week. Short day.
285 |
286 | format(E)(Date())
287 | // "Tues"
288 | */
289 | public func E() -> FormatterOf {
290 | return date(format: "E")
291 | }
292 |
293 | /**
294 | Day of week. Full name.
295 |
296 | format(E)(Date())
297 | // "Tuesday"
298 | */
299 | public func EEEE() -> FormatterOf {
300 | return date(format: "EEEE")
301 | }
302 |
303 | /**
304 | Day of week. Narrow name.
305 |
306 | format(E)(Date())
307 | // "T"
308 | */
309 | public func EEEEE() -> FormatterOf {
310 | return date(format: "EEEEE")
311 | }
312 |
313 | /**
314 | Day of week. Short name.
315 |
316 | format(E)(Date())
317 | // "Tu"
318 | */
319 | public func EEEEEE() -> FormatterOf {
320 | return date(format: "EEEEEE")
321 | }
322 |
323 | /**
324 | Day of week. Numeric value.
325 |
326 | format(e)(Date())
327 | // "1"
328 | */
329 | public func e() -> FormatterOf {
330 | return date(format: "e")
331 | }
332 |
333 | // MARK: Period
334 |
335 | /**
336 | AM or PM.
337 |
338 | format(a)(Date())
339 | // "PM"
340 | */
341 | public func a() -> FormatterOf {
342 | return date(format: "a")
343 | }
344 |
345 | // MARK: Hour
346 |
347 | /**
348 | Hour [1-12]. Use `hh` for zero padding.
349 | */
350 | public func h() -> FormatterOf {
351 | return date(format: "h")
352 | }
353 |
354 | /**
355 | Hour [0-12]. Zero-padded.
356 | */
357 | public func hh() -> FormatterOf {
358 | return date(format: "hh")
359 | }
360 |
361 | /**
362 | Hour [0-23]. Use `HH` for zero padding.
363 | */
364 | public func H() -> FormatterOf {
365 | return date(format: "H")
366 | }
367 |
368 | /**
369 | Hour [0-23]. Zero-padded.
370 | */
371 | public func HH() -> FormatterOf {
372 | return date(format: "HH")
373 | }
374 |
375 | /**
376 | Hour [0-11]. Use `KK` for zero padding.
377 | */
378 | public func K() -> FormatterOf {
379 | return date(format: "K")
380 | }
381 |
382 | /**
383 | Hour [0-11]. Zero-padded.
384 | */
385 | public func KK() -> FormatterOf {
386 | return date(format: "KK")
387 | }
388 |
389 | /**
390 | Hour [1-24]. Use `kk` for zero padding.
391 | */
392 | public func k() -> FormatterOf {
393 | return date(format: "k")
394 | }
395 |
396 | /**
397 | Hour [1-24]. Zero-padded.
398 | */
399 | public func kk() -> FormatterOf {
400 | return date(format: "kk")
401 | }
402 |
403 | // MARK: Minute
404 |
405 | /**
406 | Minute. Use `mm` for zero padding.
407 | */
408 | public func m() -> FormatterOf {
409 | return date(format: "m")
410 | }
411 |
412 | /**
413 | Minute. Zero-padded.
414 | */
415 | public func mm() -> FormatterOf {
416 | return date(format: "mm")
417 | }
418 |
419 | // MARK: Second
420 |
421 | /**
422 | Second. Use `ss` for zero padding.
423 | */
424 | public func s() -> FormatterOf {
425 | return date(format: "s")
426 | }
427 |
428 | /**
429 | Second. Zero-padded.
430 | */
431 | public func ss() -> FormatterOf {
432 | return date(format: "ss")
433 | }
434 |
435 | /**
436 | Fractional second truncated to 1 character.
437 | */
438 | public func S() -> FormatterOf {
439 | return date(format: "S")
440 | }
441 |
442 | /**
443 | Fractional second truncated to 2 characters.
444 | */
445 | public func SS() -> FormatterOf {
446 | return date(format: "SS")
447 | }
448 |
449 | /**
450 | Fractional second truncated to 3 characters.
451 | */
452 | public func SSS() -> FormatterOf {
453 | return date(format: "SSS")
454 | }
455 |
456 | /**
457 | Milliseconds in day. This field behaves _exactly_ like a composite of all time-related fields, not including
458 | the zone fields. As such, it also reflects discontinuities of those fields on DST transition days. On a day
459 | of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This reflects the fact
460 | that is must be combined with the offset field to obtain a unique local time value.
461 | */
462 | public func A() -> FormatterOf {
463 | return date(format: "A")
464 | }
465 |
466 | // MARK: Zone
467 |
468 | /**
469 | Time zone. The _short specific non-location format_. Where that is unavailable, falls back to the short
470 | localized GMT format (`O`).
471 |
472 | format(z)(Date())
473 | // "EDT"
474 | */
475 | public func z() -> FormatterOf {
476 | return date(format: "z")
477 | }
478 |
479 | /**
480 | The _long specific non-location format_. Where that is unavailable, falls back to the long localized GMT
481 | format (`OOOO`).
482 |
483 | format(zzzz)(Date())
484 | // "Eastern Daylight Time"
485 | */
486 | public func zzzz() -> FormatterOf {
487 | return date(format: "zzzz")
488 | }
489 |
490 | /**
491 | The _ISO8601 basic format_ with hours, minutes and optional seconds fields. The format is equivalent to RFC
492 | 822 zone format (when optional seconds field is absent). This is equivalent to the `xxxx` specifier.
493 |
494 | format(Z)(Date())
495 | // "-0400"
496 | */
497 | public func Z() -> FormatterOf {
498 | return date(format: "Z")
499 | }
500 |
501 | /**
502 | The _long localized GMT format_. This is equivalent to the `OOOO` specifier.
503 |
504 | format(ZZZZ)(Date())
505 | // "GMT-04:00"
506 | */
507 | public func ZZZZ() -> FormatterOf {
508 | return date(format: "ZZZZ")
509 | }
510 |
511 | /**
512 | The _ISO8601 extended format_ with hours, minutes and optional seconds fields. The ISO8601 UTC indicator "Z"
513 | is used when local time offset is 0. This is equivalent to the `XXXXX` specifier.
514 |
515 | format(ZZZZZ)(Date())
516 | // "-04:00"
517 | */
518 | public func ZZZZZ() -> FormatterOf {
519 | return date(format: "ZZZZZ")
520 | }
521 |
522 | /**
523 | The _short localized GMT format_.
524 |
525 | format(ZZZZZ)(Date())
526 | // "GMT-4"
527 | */
528 | public func O() -> FormatterOf {
529 | return date(format: "O")
530 | }
531 |
532 | /**
533 | The _long localized GMT format_.
534 |
535 | format(ZZZZZ)(Date())
536 | // "GMT-04:00"
537 | */
538 | public func OOOO() -> FormatterOf {
539 | return date(format: "OOOO")
540 | }
541 |
542 | /**
543 | The _short generic non-location format_. Where that is unavailable, falls back to the _generic location
544 | format_ (`VVVV`), then the _short localized GMT format_ as the final fallback.
545 |
546 | format(v)(Date())
547 | // "ET"
548 | */
549 | public func v() -> FormatterOf {
550 | return date(format: "v")
551 | }
552 |
553 | /**
554 | The _long generic non-location format_. Where that is unavailable, falls back to _generic location format_
555 | (`VVVV`).
556 |
557 | format(vvvv)(Date())
558 | // "Eastern Time"
559 | */
560 | public func vvvv() -> FormatterOf {
561 | return date(format: "vvvv")
562 | }
563 |
564 | /**
565 | The short time zone ID. Where that is unavailable, the special short time zone ID _unk_ (Unknown Zone) is
566 | used.
567 |
568 | format(V)(Date())
569 | // "usnyc"
570 |
571 | - note: This specifier was originally used for a variant of the short specific non-location format, but it
572 | was deprecated in the later version of this specification. In CLDR 23, the definition of the specifier was
573 | changed to designate a short time zone ID.
574 | */
575 | public func V() -> FormatterOf {
576 | return date(format: "V")
577 | }
578 |
579 | /**
580 | The long time zone ID.
581 |
582 | format(VV)(Date())
583 | // "America/New_York"
584 | */
585 | public func VV() -> FormatterOf {
586 | return date(format: "VV")
587 | }
588 |
589 | /**
590 | The exemplar city (location) for the time zone. Where that is unavailable, the localized exemplar city name
591 | for the special zone _Etc/Unknown_ is used as the fallback (for example, "Unknown City").
592 |
593 | format(VVV)(Date())
594 | // "New York"
595 | */
596 | public func VVV() -> FormatterOf {
597 | return date(format: "VVV")
598 | }
599 |
600 | /**
601 | The _generic location format_. Where that is unavailable, falls back to the _long localized GMT format_
602 | (`OOOO`).
603 |
604 | This is especially useful when presenting possible timezone choices for user selection, since the naming is
605 | more uniform than the `v` format.
606 |
607 | format(VVVV)(Date())
608 | // "New York Time"
609 |
610 | - note: Fallback is only necessary with a GMT-style Time Zone ID, like Etc/GMT-830
611 | */
612 | public func VVVV() -> FormatterOf {
613 | return date(format: "VVVV")
614 | }
615 |
616 | /**
617 | The _ISO8601 basic format_ with hours field and optional minutes field. The ISO8601 UTC indicator "Z" is
618 | used when local time offset is 0. (The same as `x`, plus "Z".)
619 |
620 | format(X)(Date())
621 | // "-04"
622 | format(X)(utc)
623 | // "Z"
624 | */
625 | public func X() -> FormatterOf {
626 | return date(format: "X")
627 | }
628 | /**
629 | The _ISO8601 basic format_ with hours and minutes fields. The ISO8601 UTC indicator "Z" is used when local
630 | time offset is 0. (The same as `xx`, plus "Z".)
631 |
632 | format(XX)(Date())
633 | // "-0400"
634 | format(XX)(utc)
635 | // "Z"
636 | */
637 | public func XX() -> FormatterOf {
638 | return date(format: "XX")
639 | }
640 | /**
641 | The _ISO8601 basic format_ with hours and minutes fields. The ISO8601 UTC indicator "Z" is used when local
642 | time offset is 0. (The same as `xxx`, plus `Z`.)
643 |
644 | format(XXX)(Date())
645 | // "-04:00"
646 | format(XXX)(utc)
647 | // "Z"
648 | */
649 | public func XXX() -> FormatterOf {
650 | return date(format: "XXX")
651 | }
652 | /**
653 | The _ISO8601 basic format_ with hours, minutes and optional seconds fields. The ISO8601 UTC indicator "Z" is
654 | used when local time offset is 0. (The same as `xxxx`, plus "Z".)
655 |
656 | format(XXXX)(Date())
657 | // "-0400"
658 | format(XXXX)(utc)
659 | // "Z"
660 |
661 | - note: The seconds field is not supported by the ISO8601 specification.
662 | */
663 | public func XXXX() -> FormatterOf {
664 | return date(format: "XXXX")
665 | }
666 | /**
667 | The _ISO8601 extended format_ with hours, minutes and optional seconds fields. The ISO8601 UTC indicator "Z"
668 | is used when local time offset is 0. (The same as `xxxxx`, plus "Z".)
669 |
670 | format(XXXXX)(Date())
671 | // "-04:00"
672 | format(XXXXX)(utc)
673 | // "Z"
674 |
675 | - note: The seconds field is not supported by the ISO8601 specification.
676 | */
677 | public func XXXXX() -> FormatterOf {
678 | return date(format: "XXXXX")
679 | }
680 |
681 | /**
682 | The _ISO8601 basic format_ with hours field and optional minutes field. (The same as `x`, plus "Z".)
683 |
684 | format(x)(Date())
685 | // "-04"
686 | */
687 | public func x() -> FormatterOf {
688 | return date(format: "x")
689 | }
690 | /**
691 | The _ISO8601 basic format_ with hours and minutes fields. (The same as `x`, plus "Z".)
692 |
693 | format(xx)(Date())
694 | // "-0400"
695 | */
696 | public func xx() -> FormatterOf {
697 | return date(format: "xx")
698 | }
699 | /**
700 | The _ISO8601 basic format_ with hours and minutes fields. (The same as `xxx`, plus `Z`.)
701 |
702 | format(xxx)(Date())
703 | // "-04:00"
704 | */
705 | public func xxx() -> FormatterOf {
706 | return date(format: "xxx")
707 | }
708 | /**
709 | The _ISO8601 basic format_ with hours, minutes and optional seconds fields. (The same as `xxxx`, plus "Z".)
710 |
711 | format(xxxx)(Date())
712 | // "-0400"
713 |
714 | - note: The seconds field is not supported by the ISO8601 specification.
715 | */
716 | public func xxxx() -> FormatterOf {
717 | return date(format: "xxxx")
718 | }
719 | /**
720 | The _ISO8601 extended format_ with hours, minutes and optional seconds fields. (The same as `xxxxx`, plus
721 | "Z".)
722 |
723 | format(xxxxx)(Date())
724 | // "-04:00"
725 |
726 | - note: The seconds field is not supported by the ISO8601 specification.
727 | */
728 | public func xxxxx() -> FormatterOf {
729 | return date(format: "xxxxx")
730 | }
731 |
--------------------------------------------------------------------------------
/Sources/Formatter.swift:
--------------------------------------------------------------------------------
1 | /**
2 | A formatter. The `Result` type means the returned value at the end. The more formatters you compose, the
3 | more this will build up arguments. _E.g._, from `Result` to `(Int) -> Result` to
4 | `(Character) -> (Int) -> Result`, etc.
5 | */
6 |
7 | public struct Formatter {
8 | public let format: (@escaping (String) -> Result) -> A
9 | }
10 |
11 | /**
12 | A formatter that transforms `Input` into `Result`.
13 | */
14 | public typealias FormatterOf = Formatter Result>
15 |
16 | /**
17 | Run the formatter to produce a `String` value.
18 | */
19 | public func format(_ formatter: Formatter) -> A {
20 | return formatter.format { string in string }
21 | }
22 |
23 | // MARK:
24 |
25 | /**
26 | Composition operator. Combines two formatters.
27 |
28 | - parameters:
29 | - lhs: a formatter
30 | - rhs: a formatter
31 | - returns: A formatter combining the left-hand side with the right-hand side.
32 | */
33 | public func % (lhs: Formatter, rhs: Formatter) -> Formatter {
34 | return Formatter { stringToResult in
35 | lhs.format { leftString in
36 | rhs.format { rightString in
37 | stringToResult(leftString + rightString)
38 | }
39 | }
40 | }
41 | }
42 |
43 | private func s(_ string: String) -> Formatter {
44 | return Formatter { stringToResult in stringToResult(string) }
45 | }
46 |
47 | /**
48 | Composition operator. Combines a formatter and a string.
49 |
50 | format(int % " luftballons")(99)
51 | // "99 luftballons"
52 |
53 | - parameters:
54 | - lhs: a formatter
55 | - rhs: a string
56 | - returns: A formatter appending the given formatter with the given string.
57 | */
58 | public func % (lhs: Formatter, rhs: String) -> Formatter {
59 | return lhs % s(rhs)
60 | }
61 |
62 | /**
63 | Composition operator. Combines a string and a formatter.
64 |
65 | format("hello " % string)("world")
66 | // "hello world"
67 |
68 | - parameters:
69 | - lhs: a string
70 | - rhs: a formatter
71 | - returns: A formatter with the given string prepended to the given formatter.
72 | */
73 | public func % (lhs: String, rhs: Formatter) -> Formatter {
74 | return s(lhs) % rhs
75 | }
76 |
77 | // MARK:
78 |
79 | /**
80 | Monoidal composition. Will append previous formatter input to the right-hand formatter.
81 |
82 | format("It's day " % D % " of " <> y % ".")(Date())
83 | // "It's day 180 of 2016."
84 |
85 | - parameters:
86 | - lhs: a formatter
87 | - rhs: a formatter of the same type
88 | - returns: A combined formatter matching the types of the given formatters.
89 | */
90 | public func <> (lhs: FormatterOf, rhs: FormatterOf) -> FormatterOf {
91 | return Formatter { stringToResult in
92 | { input in
93 | lhs.format { leftString in
94 | rhs.format { rightString in
95 | stringToResult(leftString + rightString)
96 | }(input)
97 | }(input)
98 | }
99 | }
100 | }
101 |
102 | // MARK:
103 |
104 | /**
105 | Function compose two formatters. Will feed the result of one formatter into another.
106 |
107 | format(left(2, "0") .% hex)(10)
108 | // "0a"
109 |
110 | - parameters:
111 | - lhs: a formatter that takes a string
112 | - rhs: a formatter
113 | */
114 | public func .% (lhs: Formatter B>, rhs: Formatter) -> Formatter {
115 | return Formatter { stringToResult in rhs.format(lhs.format(stringToResult)) }
116 | }
117 |
118 | // MARK:
119 | // paren-reducing overloads: these can go away when generic accessors (_e.g._, `var string`) are supported
120 |
121 | public func format(_ formatter: () -> Formatter) -> A {
122 | return formatter().format { $0 }
123 | }
124 |
125 | public func % (lhs: () -> Formatter, rhs: String) -> Formatter {
126 | return lhs() % s(rhs)
127 | }
128 |
129 | public func % (lhs: String, rhs: () -> Formatter) -> Formatter {
130 | return s(lhs) % rhs()
131 | }
132 |
133 | public func % (lhs: Formatter, rhs: () -> Formatter) -> Formatter {
134 | return lhs % rhs()
135 | }
136 |
137 | public func % (lhs: () -> Formatter, rhs: Formatter) -> Formatter {
138 | return lhs() % rhs
139 | }
140 |
141 | public func <> (lhs: () -> FormatterOf, rhs: FormatterOf) -> FormatterOf {
142 | return lhs() <> rhs
143 | }
144 |
145 | public func <> (lhs: FormatterOf, rhs: () -> FormatterOf) -> FormatterOf {
146 | return lhs <> rhs()
147 | }
148 |
149 | public func <> (lhs: () -> FormatterOf, rhs: () -> FormatterOf) -> FormatterOf {
150 | return lhs() <> rhs()
151 | }
152 |
153 | public func .% (lhs: () -> Formatter B>, rhs: Formatter) -> Formatter {
154 | return lhs() .% rhs
155 | }
156 |
157 | public func .% (lhs: Formatter B>, rhs: () -> Formatter) -> Formatter {
158 | return lhs .% rhs()
159 | }
160 |
161 | public func .% (lhs: () -> Formatter B>, rhs: () -> Formatter)
162 | -> Formatter {
163 |
164 | return lhs() .% rhs()
165 | }
166 |
--------------------------------------------------------------------------------
/Sources/Formatters.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // MARK: String types
4 |
5 | /**
6 | Render a string.
7 | */
8 | public func string() -> FormatterOf {
9 | return Formatter { f in { string in f(string) } }
10 | }
11 |
12 | /**
13 | Render the `description` of a `CustomStringConvertible`.
14 | */
15 | public func describe() -> FormatterOf {
16 | return Formatter { f in { convertible in f(convertible.description) } }
17 | }
18 |
19 | /**
20 | Render the `debugDescription` of a `CustomDebugStringConvertible`.
21 | */
22 | public func debug() -> FormatterOf {
23 | return Formatter { f in { convertible in f(convertible.debugDescription) } }
24 | }
25 |
26 | /**
27 | Render a character.
28 | */
29 | public func char() -> FormatterOf {
30 | return Formatter { f in { char in f(String(char)) } }
31 | }
32 |
33 | /**
34 | Render a character code.
35 |
36 | format("Got: '" % char % "' (" <> asInt % ")")("a")
37 | // "Got: 'a' (92)"
38 | */
39 | public func asInt() -> FormatterOf {
40 | return Formatter { f in { char in f(String(String(char).utf8.first!)) } }
41 | }
42 |
43 | /**
44 | Render uppercased.
45 | */
46 | public func upper() -> FormatterOf