Initial commit
This commit is contained in:
201
Pods/KituraContracts/LICENSE
generated
Normal file
201
Pods/KituraContracts/LICENSE
generated
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
87
Pods/KituraContracts/README.md
generated
Normal file
87
Pods/KituraContracts/README.md
generated
Normal file
@ -0,0 +1,87 @@
|
||||
<p align="center">
|
||||
<a href="https://www.kitura.io/">
|
||||
<img src="https://raw.githubusercontent.com/IBM-Swift/Kitura/master/Sources/Kitura/resources/kitura-bird.svg?sanitize=true" height="100" alt="Kitura">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://ibm-swift.github.io/KituraContracts/index.html">
|
||||
<img src="https://img.shields.io/badge/apidoc-KituraContracts-1FBCE4.svg?style=flat" alt="APIDoc">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/IBM-Swift/KituraContracts">
|
||||
<img src="https://travis-ci.org/IBM-Swift/KituraContracts.svg?branch=master" alt="Build Status - Master">
|
||||
</a>
|
||||
<img src="https://img.shields.io/badge/os-macOS-green.svg?style=flat" alt="macOS">
|
||||
<img src="https://img.shields.io/badge/os-linux-green.svg?style=flat" alt="Linux">
|
||||
<img src="https://img.shields.io/badge/license-Apache2-blue.svg?style=flat" alt="Apache 2">
|
||||
<a href="http://swift-at-ibm-slack.mybluemix.net/">
|
||||
<img src="http://swift-at-ibm-slack.mybluemix.net/badge.svg" alt="Slack Status">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# KituraContracts
|
||||
|
||||
## Summary
|
||||
|
||||
KituraContracts is a library containing type definitions shared by client (e.g. [KituraKit](https://ibm-swift.github.io/KituraKit/)) and server (e.g. [Kitura](https://ibm-swift.github.io/Kitura)) code. These shared type definitions include [Codable Closure Aliases](https://ibm-swift.github.io/KituraContracts/Typealiases.html), [RequestError](https://ibm-swift.github.io/KituraContracts/Structs/RequestError.html), [QueryEncoder](https://ibm-swift.github.io/KituraContracts/Classes/QueryEncoder.html), [QueryDecoder](https://ibm-swift.github.io/KituraContracts/Classes/QueryDecoder.html), [Coder](https://ibm-swift.github.io/KituraContracts/Classes/Coder.html), [Identifier Protocol](https://ibm-swift.github.io/KituraContracts/Protocols/Identifier.html#/s:15KituraContracts10IdentifierP5valueSSv) and [Extensions](https://ibm-swift.github.io/KituraContracts/Extensions.html#/s:SS) to String and Int, which add conformity to the Identifier protocol.
|
||||
|
||||
## Usage
|
||||
|
||||
KituraContracts represents the types and protocols that are common to both the [Kitura](https://github.com/IBM-Swift/Kitura) server and [KituraKit](https://github.com/IBM-Swift/KituraKit) client side library. If you are using Kitura or KituraKit, your project does not need to depend on KituraContracts explicitly.
|
||||
|
||||
#### Add dependencies
|
||||
|
||||
Add the `KituraContracts` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `KituraContracts` [release](https://github.com/IBM-Swift/KituraContracts/releases).
|
||||
|
||||
```swift
|
||||
.package(url: "https://github.com/IBM-Swift/KituraContracts.git", from: "x.x.x")
|
||||
```
|
||||
|
||||
Add `KituraContracts` to your target's dependencies:
|
||||
|
||||
```swift
|
||||
.target(name: "example", dependencies: ["KituraContracts"]),
|
||||
```
|
||||
|
||||
#### Import package
|
||||
|
||||
```swift
|
||||
import KituraContracts
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
This example, shows how to use a shared type definition for `RequestError` within a router POST method on `users`. If no errors occurred and you have a `User` you can respond with the user and pass nil as the `RequestError?` value. If there has been an error you can respond with an appropriate error and pass nil for the `User?`.
|
||||
|
||||
````swift
|
||||
public struct User: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
router.post("/users") { (user: User, respondWith: (User?, RequestError?) -> Void) in
|
||||
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(user, nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(nil, .internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## Swift version
|
||||
|
||||
The 1.x.x releases were tested on macOS and Linux using the Swift 4.1 binaries. Please note that this is the default version of Swift that is included in [Xcode 9.3](https://developer.apple.com/xcode/).
|
||||
|
||||
## API Documentation
|
||||
For more information visit our [API reference](https://ibm-swift.github.io/KituraContracts/index.html).
|
||||
|
||||
## Community
|
||||
|
||||
We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team!
|
||||
|
||||
## License
|
||||
|
||||
This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/KituraContracts/blob/master/LICENSE).
|
||||
28
Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift
generated
Normal file
28
Pods/KituraContracts/Sources/KituraContracts/BodyDecoder.swift
generated
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2018
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A class that conforms to `BodyDecoder` must be able to decode from `Data` into a `Codable` type.
|
||||
This class can then be used to produce input objects for a Codable route.
|
||||
*/
|
||||
public protocol BodyDecoder: AnyObject {
|
||||
/// Decode a `Decodable` type from a `Data`, using this `BodyDecoder`.
|
||||
func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T
|
||||
}
|
||||
|
||||
extension JSONDecoder: BodyDecoder {}
|
||||
28
Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift
generated
Normal file
28
Pods/KituraContracts/Sources/KituraContracts/BodyEncoder.swift
generated
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2018
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A class that conforms to `BodyEncoder` must be able to encode a `Codable` type into `Data`.
|
||||
This class can then be used to produce output objects for a Codable route.
|
||||
*/
|
||||
public protocol BodyEncoder: AnyObject {
|
||||
/// Encode an `Encodable` type to a `Data`, using this `BodyEncoder`.
|
||||
func encode<T : Encodable>(_ value: T) throws -> Data
|
||||
}
|
||||
extension JSONEncoder: BodyEncoder {}
|
||||
|
||||
78
Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift
generated
Normal file
78
Pods/KituraContracts/Sources/KituraContracts/BodyFormat.swift
generated
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2018
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A set of values representing the format of a response body.
|
||||
|
||||
This struct is intended to be "enum-like" and values should be
|
||||
accessed via the public static stored properties.
|
||||
|
||||
- Note: An enum was not used here because currently enums are
|
||||
always exhaustive. This means adding a case to an enum
|
||||
is a breaking change. In order to keep such additions
|
||||
non-breaking we have used an "enum-like" struct instead.
|
||||
This means code using `BodyFormat` should always handle
|
||||
unrecognised `BodyFormat` values (eg in a default case
|
||||
of a switch). `UnsupportedBodyFormatError` may be used
|
||||
in this situation.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let format = BodyFormat.json
|
||||
````
|
||||
*/
|
||||
public struct BodyFormat: Equatable {
|
||||
|
||||
/**
|
||||
A String value of the type that the body format will be represented in, which is used to ensure that both the left-hand side and the right-hand side are of the same type in the response body.
|
||||
*/
|
||||
public let type: String
|
||||
|
||||
private init(_ type: String) {
|
||||
self.type = type
|
||||
}
|
||||
|
||||
/**
|
||||
This function checks that both the left-hand side and the right-hand side of the response body are of the same type.
|
||||
*/
|
||||
public static func == (_ lhs: BodyFormat, _ rhs: BodyFormat) -> Bool {
|
||||
return lhs.type == rhs.type
|
||||
}
|
||||
|
||||
/**
|
||||
The JSON representation of the response body.
|
||||
*/
|
||||
public static let json = BodyFormat("application/json")
|
||||
}
|
||||
|
||||
/**
|
||||
An error that may be thrown when a particular instance of `BodyFormat`
|
||||
is not supported.
|
||||
*/
|
||||
public struct UnsupportedBodyFormatError: Error {
|
||||
/**
|
||||
The format of the body.
|
||||
*/
|
||||
public let bodyFormat: BodyFormat
|
||||
/**
|
||||
Initialize `UnsupportedBodyFormatError` with the format of the body.
|
||||
*/
|
||||
public init(_ bodyFormat: BodyFormat) {
|
||||
self.bodyFormat = bodyFormat
|
||||
}
|
||||
}
|
||||
260
Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift
generated
Normal file
260
Pods/KituraContracts/Sources/KituraContracts/ClosureAliases.swift
generated
Normal file
@ -0,0 +1,260 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
// MARK: Codable Type Aliases
|
||||
|
||||
/**
|
||||
The `ResultClosure` is used by other `Codable` aliases when responding with only a `RequestError` is needed.
|
||||
|
||||
The following two typealiases make use of `ResultClosure`:
|
||||
````swift
|
||||
public typealias NonCodableClosure = (@escaping ResultClosure) -> Void
|
||||
|
||||
public typealias IdentifierNonCodableClosure<Id: Identifier> = (Id, @escaping ResultClosure) -> Void
|
||||
````
|
||||
*/
|
||||
public typealias ResultClosure = (RequestError?) -> Void
|
||||
|
||||
/**
|
||||
The `CodableResultClosure` is used by other `Codable` aliases when responding with an object which conforms to `Codable`, or a `RequestError` is needed.
|
||||
|
||||
The following two typealiases make use of `CodableResultClosure`:
|
||||
````swift
|
||||
public typealias IdentifierCodableClosure<Id: Identifier, I: Codable, O: Codable> = (Id, I, @escaping CodableResultClosure<O>) -> Void
|
||||
|
||||
public typealias CodableClosure<I: Codable, O: Codable> = (I, @escaping CodableResultClosure<O>) -> Void
|
||||
````
|
||||
*/
|
||||
public typealias CodableResultClosure<O: Codable> = (O?, RequestError?) -> Void
|
||||
|
||||
/**
|
||||
The `CodableArrayResultClosure` is used by other `Codable` aliases when responding with an array of objects which conform to `Codable`, or a `RequestError` is needed.
|
||||
|
||||
The following typealias makes use of `CodableArrayResultClosure`:
|
||||
````swift
|
||||
public typealias CodableArrayClosure<O: Codable> = (@escaping CodableArrayResultClosure<O>) -> Void
|
||||
````
|
||||
*/
|
||||
public typealias CodableArrayResultClosure<O: Codable> = ([O]?, RequestError?) -> Void
|
||||
|
||||
/**
|
||||
The `IdentifierCodableArrayResultClosure` is used by other `Codable` aliases when responding with an array of tuples containing an identifier and a `Codable` object, or a `RequestError`.
|
||||
|
||||
The following typealias makes use of `IdentifierCodableArrayResultClosure`:
|
||||
````swift
|
||||
public typealias CodableIdentifierClosure<I: Codable, Id: Identifier, O: Codable> = (I, @escaping IdentifierCodableResultClosure<[Id, O]?>) -> Void
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierCodableArrayResultClosure<Id: Identifier, O: Codable> = ([(Id, O)]?, RequestError?) -> Void
|
||||
|
||||
/**
|
||||
This is used to perform a series of actions which use an object conforming to `Identifier` and an object conforming to `Codable`. After which you want to respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or respond with an `Identifier` or `RequestError`.
|
||||
|
||||
The following typealias makes use of `IdentifierCodableResultClosure`:
|
||||
````swift
|
||||
public typealias CodableIdentifierClosure<I: Codable, Id: Identifier, O: Codable> = (I, @escaping IdentifierCodableResultClosure<Id, O>) -> Void
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierCodableResultClosure<Id: Identifier, O: Codable> = (Id?, O?, RequestError?) -> Void
|
||||
|
||||
/**
|
||||
The `IdentifierCodableClosure` is used to perform a series of actions utilising an object conforming to `Identifier` and an object conforming to 'Codable', then respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or responding with a `RequestError` in the form of a `CodableResultClosure`.
|
||||
|
||||
By default `Int` has conformity to `Identifier`. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error, passing nil for the `User?`. If no errors occurred and you have a `User`, you can just respond with the user, passing nil as the `RequestError?` value.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
public struct User: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
var userStore: [Int, User] = [...]
|
||||
|
||||
router.put("/users") { (id: Int, user: User, respondWith: (User?, RequestError?) -> Void) in
|
||||
guard let oldUser = self.userStore[id] else {
|
||||
respondWith(nil, .notFound)
|
||||
return
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
respondWith(user, nil)
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierCodableClosure<Id: Identifier, I: Codable, O: Codable> = (Id, I, @escaping CodableResultClosure<O>) -> Void
|
||||
|
||||
/**
|
||||
The `CodableClosure` is used to perform a series of actions utilising an object conforming to `Identifier`, then respond with an object which conforms to `Codable`, which is of the same type as the object passed as a parameter, or responding with a `RequestError` in the form of a `CodableResultClosure`.
|
||||
|
||||
If no errors occurred and you have a `User`, you can just respond with the user by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `User?`.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
public struct User: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
router.post("/users") { (user: User, respondWith: (User?, RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(user, nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(nil, .internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias CodableClosure<I: Codable, O: Codable> = (I, @escaping CodableResultClosure<O>) -> Void
|
||||
|
||||
/**
|
||||
The `CodableIdentifierClosure` is used to perform a series of actions utilising an object conforming to `Identifier`, then respond with an object which conforms to `Codable`, and/or an object conforming to `Identifier` or responding with a `RequestError` in the form of a `IdentifierCodableResultClosure`.
|
||||
|
||||
If no errors occurred and you have a `User` and the corresponding identifier, you can just respond with the identifier and user, and pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for `Int?` and nil for `User?`.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
public struct User: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
router.post("/users") { (user: User, respondWith: (Int?, User?, RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(id, user, nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(nil, nil, .internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias CodableIdentifierClosure<I: Codable, Id: Identifier, O: Codable> = (I, @escaping IdentifierCodableResultClosure<Id, O>) -> Void
|
||||
|
||||
/**
|
||||
The `NonCodableClosure` is used to perform a series of actions then respond with a `RequestError` in the form of a `ResultClosure`.
|
||||
|
||||
If no errors occurred you can just pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
router.delete("/users") { (respondWith: (RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(nil)
|
||||
} else {
|
||||
respondWith(.internalServerError)
|
||||
...
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias NonCodableClosure = (@escaping ResultClosure) -> Void
|
||||
|
||||
/**
|
||||
The `IdentifierNonCodableClosure` is used to perform a series of actions utilising an object which conforms to `Identifier`, then respond with a `RequestError` in the form of a `ResultClosure`.
|
||||
|
||||
If no errors occurred you can just pass nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
router.delete("/users") { (id: Int, respondWith: (RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(.internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierNonCodableClosure<Id: Identifier> = (Id, @escaping ResultClosure) -> Void
|
||||
|
||||
/**
|
||||
The `CodableArrayClosure` is used to perform a series of actions then respond with an array of objects conforming to `Codable` or a `RequestError` in the form of a `CodableArrayResultClosure`.
|
||||
|
||||
If no errors occurred and you have an array of `Users` you can just respond with the users by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `[User]?`.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
router.get("/users") { (respondWith: ([User]?, RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith(users, nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(nil, .internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias CodableArrayClosure<O: Codable> = (@escaping CodableArrayResultClosure<O>) -> Void
|
||||
|
||||
/**
|
||||
The `IdentifierCodableArrayClosure` is used to perform a series of actions then respond with an array of tuples containing an identifier and a Codable object, or a `RequestError`, in the form of a `IdentifierCodableArrayResultClosure`.
|
||||
|
||||
If no errors occurred and you have an array of `Users` you can just respond with the users by passing nil as the `RequestError?` value. In this example, if there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `[User]?`.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
router.get("/users") { (respondWith: ([(Int, User)]?, RequestError?) -> Void) in
|
||||
if databaseConnectionIsOk {
|
||||
...
|
||||
respondWith([(Int, User)], nil)
|
||||
} else {
|
||||
...
|
||||
respondWith(nil, .internalServerError)
|
||||
}
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierCodableArrayClosure<Id: Identifier, O: Codable> = (@escaping IdentifierCodableArrayResultClosure<Id, O>) -> Void
|
||||
|
||||
/**
|
||||
The `SimpleCodableClosure` is used to perform a series of actions, then respond with an object conforming to `Codable` or a `RequestError` in the form of a `CodableResultClosure`.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
public struct Status: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
router.get("/status") { (respondWith: (Status?, RequestError?) -> Void) in
|
||||
...
|
||||
respondWith(status, nil)
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias SimpleCodableClosure<O: Codable> = (@escaping CodableResultClosure<O>) -> Void
|
||||
|
||||
/**
|
||||
The `IdentifierSimpleCodableClosure` is used to perform a series of actions utilising an object which conforms to `Identifier`, then respond with an object conforming to `Codable` or a `RequestError` in the form of a `CodableResultClosure`.
|
||||
|
||||
If there has been an error you can use the `respondWith` call to respond with an appropriate error and passing nil for the `User?`. In this example, if no errors occurred and you have a `User` you can just respond with the user by passing nil as the `RequestError?` value.
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
public struct User: Codable {
|
||||
...
|
||||
}
|
||||
|
||||
var userStore: [Int, User] = (...)
|
||||
|
||||
router.get("/users") { (id: Int, respondWith: (User?, RequestError?) -> Void) in
|
||||
guard let user = self.userStore[id] else {
|
||||
respondWith(nil, .notFound)
|
||||
return
|
||||
}
|
||||
...
|
||||
respondWith(user, nil)
|
||||
}
|
||||
````
|
||||
*/
|
||||
public typealias IdentifierSimpleCodableClosure<Id: Identifier, O: Codable> = (Id, @escaping CodableResultClosure<O>) -> Void
|
||||
68
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift
generated
Normal file
68
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Coder.swift
generated
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright IBM Corporation 2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Class defining shared resources for the [QueryDecoder](https://github.com/IBM-Swift/KituraContracts/blob/master/Sources/KituraContracts/CodableQuery/QueryDecoder.swift) and [QueryEncoder](https://github.com/IBM-Swift/KituraContracts/blob/master/Sources/KituraContracts/CodableQuery/QueryEncoder.swift).
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let date = Coder.defaultDateFormatter.date(from: "2017-10-31T16:15:56+0000")!
|
||||
````
|
||||
*/
|
||||
public class Coder {
|
||||
|
||||
@available(*, deprecated, message: "Use Coder.defaultDateFormatter instead")
|
||||
public let dateFormatter: DateFormatter = Coder.defaultDateFormatter
|
||||
|
||||
/**
|
||||
The default [DateFormatter](https://developer.apple.com/documentation/foundation/dateformatter) used for encoding and decoding query parameters. It uses the "UTC" timezone and "yyyy-MM-dd'T'HH:mm:ssZ" date format.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let date = Coder.defaultDateFormatter.date(from: "2017-10-31T16:15:56+0000")
|
||||
````
|
||||
*/
|
||||
public static let defaultDateFormatter: DateFormatter = {
|
||||
let value = DateFormatter()
|
||||
value.timeZone = TimeZone(identifier: "UTC")
|
||||
value.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
|
||||
return value
|
||||
}()
|
||||
|
||||
/**
|
||||
Initializes a `Coder` instance with a `DateFormatter`
|
||||
using the "UTC" timezone and "yyyy-MM-dd'T'HH:mm:ssZ" date format.
|
||||
*/
|
||||
public init() {}
|
||||
|
||||
/**
|
||||
Helper method to extract the field name from a `CodingKey` array.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
````
|
||||
*/
|
||||
public static func getFieldName(from codingPath: [CodingKey]) -> String {
|
||||
#if swift(>=4.1)
|
||||
return codingPath.compactMap({$0.stringValue}).joined(separator: ".")
|
||||
#else
|
||||
return codingPath.flatMap({$0.stringValue}).joined(separator: ".")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
353
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift
generated
Normal file
353
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/Extensions.swift
generated
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright IBM Corporation 2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import LoggerAPI
|
||||
|
||||
/// Codable String Conversion Extension.
|
||||
extension String {
|
||||
|
||||
/// Converts the given String to an Int?.
|
||||
public var int: Int? {
|
||||
return Int(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Int8?.
|
||||
public var int8: Int8? {
|
||||
return Int8(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Int16?.
|
||||
public var int16: Int16? {
|
||||
return Int16(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Int32?.
|
||||
public var int32: Int32? {
|
||||
return Int32(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Int64?.
|
||||
public var int64: Int64? {
|
||||
return Int64(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a UInt?.
|
||||
public var uInt: UInt? {
|
||||
return UInt(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a UInt8?.
|
||||
public var uInt8: UInt8? {
|
||||
return UInt8(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a UInt16?.
|
||||
public var uInt16: UInt16? {
|
||||
return UInt16(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a UInt32?.
|
||||
public var uInt32: UInt32? {
|
||||
return UInt32(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a UInt64?.
|
||||
public var uInt64: UInt64? {
|
||||
return UInt64(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Float?.
|
||||
public var float: Float? {
|
||||
return Float(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Double?.
|
||||
public var double: Double? {
|
||||
return Double(self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a Bool?.
|
||||
public var boolean: Bool? {
|
||||
return !self.isEmpty ? Bool(self) : false
|
||||
}
|
||||
|
||||
/// Converts the given String to a String.
|
||||
public var string: String {
|
||||
return self
|
||||
}
|
||||
|
||||
/// Converts the given String to an [Int]?.
|
||||
public var intArray: [Int]? {
|
||||
return decodeArray(Int.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [Int8]?.
|
||||
public var int8Array: [Int8]? {
|
||||
return decodeArray(Int8.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [Int16]?.
|
||||
public var int16Array: [Int16]? {
|
||||
return decodeArray(Int16.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [Int32]?.
|
||||
public var int32Array: [Int32]? {
|
||||
return decodeArray(Int32.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [Int64]?.
|
||||
public var int64Array: [Int64]? {
|
||||
return decodeArray(Int64.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [UInt]?.
|
||||
public var uIntArray: [UInt]? {
|
||||
return decodeArray(UInt.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [UInt8]?.
|
||||
public var uInt8Array: [UInt8]? {
|
||||
return decodeArray(UInt8.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [UInt16]?.
|
||||
public var uInt16Array: [UInt16]? {
|
||||
return decodeArray(UInt16.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [UInt32]?.
|
||||
public var uInt32Array: [UInt32]? {
|
||||
return decodeArray(UInt32.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to an [UInt64]?.
|
||||
public var uInt64Array: [UInt64]? {
|
||||
return decodeArray(UInt64.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a [Float]?.
|
||||
public var floatArray: [Float]? {
|
||||
return decodeArray(Float.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a [Double]?.
|
||||
public var doubleArray: [Double]? {
|
||||
return decodeArray(Double.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a [Bool]?.
|
||||
public var booleanArray: [Bool]? {
|
||||
return decodeArray(Bool.self)
|
||||
}
|
||||
|
||||
/// Converts the given String to a [String].
|
||||
public var stringArray: [String] {
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
return strs
|
||||
}
|
||||
|
||||
/**
|
||||
Method used to decode a string into the given type T.
|
||||
|
||||
- Parameter type: The Decodable type to convert the string into.
|
||||
- Returns: The Date? object. Some on success / nil on failure.
|
||||
*/
|
||||
public func decodable<T: Decodable>(_ type: T.Type) -> T? {
|
||||
guard let data = self.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
let obj: T? = try? JSONDecoder().decode(type, from: data)
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
Converts the given String to a Date?.
|
||||
|
||||
- Parameter formatter: The designated DateFormatter to convert the string with.
|
||||
- Returns: The Date? object. Some on success / nil on failure.
|
||||
*/
|
||||
public func date(_ formatter: DateFormatter) -> Date? {
|
||||
return formatter.date(from: self)
|
||||
}
|
||||
|
||||
/**
|
||||
Converts the given String to a [Date]?.
|
||||
|
||||
- Parameter formatter: The designated DateFormatter to convert the string with.
|
||||
- Returns: The [Date]? object. Some on success / nil on failure.
|
||||
*/
|
||||
public func dateArray(_ formatter: DateFormatter) -> [Date]? {
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
let dates = strs.map { formatter.date(from: $0) }.filter { $0 != nil }.map { $0! }
|
||||
if dates.count == strs.count {
|
||||
return dates
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
Converts the given String to a [Date]? object using the dateDecodingStrategy supplied.
|
||||
|
||||
- Parameter formatter: The designated `DateFormatter` to convert the string with.
|
||||
- Parameter decoderStrategy: The `JSON.dateDecodingStrategy` that should be used to decode the specifed Date. Default is set to .formatted with default dateFormatter.
|
||||
- Parameter decoder: The `Decoder` parameter is only used for the custom strategy.
|
||||
- Returns: The [Date]? object. Some on success / nil on failure.
|
||||
*/
|
||||
public func dateArray(decoderStrategy: JSONDecoder.DateDecodingStrategy = .formatted(Coder.defaultDateFormatter), decoder: Decoder?=nil) -> [Date]? {
|
||||
|
||||
switch decoderStrategy {
|
||||
case .formatted(let formatter):
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
let dates = strs.map { formatter.date(from: $0) }.filter { $0 != nil }.map { $0! }
|
||||
if dates.count == strs.count {
|
||||
return dates
|
||||
}
|
||||
return nil
|
||||
case .deferredToDate:
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
#if swift(>=4.1)
|
||||
let dbs = strs.compactMap(Double.init)
|
||||
#else
|
||||
let dbs = strs.flatMap(Double.init)
|
||||
#endif
|
||||
let dates = dbs.map { Date(timeIntervalSinceReferenceDate: $0) }
|
||||
if dates.count == dbs.count {
|
||||
return dates
|
||||
}
|
||||
return nil
|
||||
case .secondsSince1970:
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
#if swift(>=4.1)
|
||||
let dbs = strs.compactMap(Double.init)
|
||||
#else
|
||||
let dbs = strs.flatMap(Double.init)
|
||||
#endif
|
||||
let dates = dbs.map { Date(timeIntervalSince1970: $0) }
|
||||
if dates.count == dbs.count {
|
||||
return dates
|
||||
}
|
||||
return nil
|
||||
case .millisecondsSince1970:
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
#if swift(>=4.1)
|
||||
let dbs = strs.compactMap(Double.init)
|
||||
#else
|
||||
let dbs = strs.flatMap(Double.init)
|
||||
#endif
|
||||
let dates = dbs.map { Date(timeIntervalSince1970: ($0)/1000) }
|
||||
if dates.count == dbs.count {
|
||||
return dates
|
||||
}
|
||||
return nil
|
||||
case .iso8601:
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
let dates = strs.map { _iso8601Formatter.date(from: $0) }
|
||||
if dates.count == strs.count {
|
||||
return dates as? [Date]
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
case .custom(let closure):
|
||||
var dateArray: [Date] = []
|
||||
guard let decoder = decoder else {return dateArray}
|
||||
var fieldValueArray = self.split(separator: ",")
|
||||
for _ in fieldValueArray {
|
||||
// Call closure to decode value
|
||||
guard let date = try? closure(decoder) else {
|
||||
return nil
|
||||
}
|
||||
dateArray.append(date)
|
||||
// Delete from array after use
|
||||
fieldValueArray.removeFirst()
|
||||
}
|
||||
return dateArray
|
||||
#if swift(>=5) && !os(Linux)
|
||||
@unknown default:
|
||||
Log.error("Decoding strategy not found")
|
||||
fatalError()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// Helper Method to decode a string to an LosslessStringConvertible array types.
|
||||
private func decodeArray<T: LosslessStringConvertible>(_ type: T.Type) -> [T]? {
|
||||
let strs: [String] = self.components(separatedBy: ",")
|
||||
let values: [T] = strs.map { T($0) }.filter { $0 != nil }.map { $0! }
|
||||
return values.count == strs.count ? values : nil
|
||||
}
|
||||
|
||||
/// Parses percent encoded string into query parameters with comma-separated
|
||||
/// values.
|
||||
var urlDecodedFieldValuePairs: [String: String] {
|
||||
var result: [String: String] = [:]
|
||||
for item in self.components(separatedBy: "&") {
|
||||
let (key, value) = item.keyAndDecodedValue
|
||||
if let value = value {
|
||||
// If value already exists for this key, append it
|
||||
if let existingValue = result[key] {
|
||||
result[key] = "\(existingValue),\(value)"
|
||||
}
|
||||
else {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Splits a URL-encoded key and value pair (e.g. "foo=bar") into a tuple
|
||||
/// with corresponding "key" and "value" values, with the value being URL
|
||||
/// unencoded.
|
||||
var keyAndDecodedValue: (key: String, value: String?) {
|
||||
guard let range = self.range(of: "=") else {
|
||||
return (key: self, value: nil)
|
||||
}
|
||||
let key = String(self[..<range.lowerBound])
|
||||
let value = String(self[range.upperBound...])
|
||||
|
||||
let valueReplacingPlus = value.replacingOccurrences(of: "+", with: " ")
|
||||
let decodedValue = valueReplacingPlus.removingPercentEncoding
|
||||
if decodedValue == nil {
|
||||
Log.warning("Unable to decode query parameter \(key) (coded value: \(valueReplacingPlus)")
|
||||
}
|
||||
return (key: key, value: decodedValue ?? valueReplacingPlus)
|
||||
}
|
||||
}
|
||||
|
||||
// ISO8601 Formatter used for formatting ISO8601 dates.
|
||||
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
|
||||
var _iso8601Formatter: ISO8601DateFormatter = {
|
||||
let formatter = ISO8601DateFormatter()
|
||||
formatter.formatOptions = .withInternetDateTime
|
||||
return formatter
|
||||
}()
|
||||
|
||||
enum DateError: Error {
|
||||
case unknownStrategy
|
||||
}
|
||||
|
||||
extension DateError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .unknownStrategy:
|
||||
return("Date encoding or decoding strategy not known.")
|
||||
}
|
||||
}
|
||||
}
|
||||
408
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryDecoder.swift
generated
Normal file
408
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryDecoder.swift
generated
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright IBM Corporation 2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import LoggerAPI
|
||||
|
||||
/**
|
||||
Query Parameter Decoder decodes a `[String: String]` object to a `Decodable` object instance. The decode function takes the `Decodable` object as a parameter to decode the dictionary into.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let dict = ["intField": "23", "stringField": "a string", "intArray": "1,2,3", "dateField": "2017-10-31T16:15:56+0000", "optionalDateField": "2017-10-31T16:15:56+0000", "nested": "{\"nestedIntField\":333,\"nestedStringField\":\"nested string\"}" ]
|
||||
|
||||
guard let query = try? QueryDecoder(dictionary: dict).decode(MyQuery.self) else {
|
||||
print("Failed to decode query to MyQuery Object")
|
||||
return
|
||||
}
|
||||
````
|
||||
|
||||
### Decoding Empty Values:
|
||||
When an HTML form is sent with an empty or unchecked field, the corresponding key/value pair is sent with an empty value (i.e. `&key1=&key2=`).
|
||||
The corresponding mapping to Swift types performed by `QueryDecoder` is as follows:
|
||||
- Any Optional type (including `String?`) defaults to `nil`
|
||||
- Non-optional `String` successfully decodes to `""`
|
||||
- Non-optional `Bool` decodes to `false`
|
||||
- All other non-optional types throw a decoding error
|
||||
*/
|
||||
public class QueryDecoder: Coder, Decoder, BodyDecoder {
|
||||
|
||||
/**
|
||||
The coding key path.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
````
|
||||
*/
|
||||
public var codingPath: [CodingKey] = []
|
||||
|
||||
/**
|
||||
The coding user info key.
|
||||
*/
|
||||
public var userInfo: [CodingUserInfoKey : Any] = [:]
|
||||
|
||||
/**
|
||||
A `[String: String]` dictionary.
|
||||
*/
|
||||
public var dictionary: [String : String]
|
||||
|
||||
|
||||
// A `JSONDecoder.DateDecodingStrategy` date decoder used to determine what strategy
|
||||
// to use when decoding the specific date.
|
||||
private var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy
|
||||
/**
|
||||
Initializer with an empty dictionary for decoding from Data.
|
||||
*/
|
||||
public override init () {
|
||||
self.dateDecodingStrategy = .formatted(Coder.defaultDateFormatter)
|
||||
self.dictionary = [:]
|
||||
super.init()
|
||||
}
|
||||
/**
|
||||
Initializer with a `[String : String]` dictionary.
|
||||
*/
|
||||
public init(dictionary: [String : String]) {
|
||||
self.dateDecodingStrategy = .formatted(Coder.defaultDateFormatter)
|
||||
self.dictionary = dictionary
|
||||
super.init()
|
||||
}
|
||||
/**
|
||||
Decode URL encoded data by mapping to its Decodable object representation.
|
||||
|
||||
- Parameter type: The Decodable type to the Data will be decoded as.
|
||||
- Parameter from: The Data to be decoded as the Decodable type.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let query = try? QueryDecoder().decode(MyQuery.self, from queryData) else {
|
||||
print("Failed to decode query to MyQuery Object")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
|
||||
guard let urlString = String(data: data, encoding: .utf8) else {
|
||||
throw RequestError.unprocessableEntity
|
||||
}
|
||||
let decoder = QueryDecoder(dictionary: urlString.urlDecodedFieldValuePairs)
|
||||
decoder.dateDecodingStrategy = dateDecodingStrategy
|
||||
if let Q = T.self as? QueryParams.Type {
|
||||
decoder.dateDecodingStrategy = Q.dateDecodingStrategy
|
||||
}
|
||||
return try T(from: decoder)
|
||||
}
|
||||
|
||||
/**
|
||||
Decodes a `[String: String]` mapping to its Decodable object representation.
|
||||
|
||||
- Parameter value: The Decodable object to decode the dictionary into.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let query = try? QueryDecoder(dictionary: expectedDict).decode(MyQuery.self) else {
|
||||
print("Failed to decode query to MyQuery Object")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func decode<T: Decodable>(_ type: T.Type) throws -> T {
|
||||
if let Q = T.self as? QueryParams.Type {
|
||||
dateDecodingStrategy = Q.dateDecodingStrategy
|
||||
}
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
let fieldValue = dictionary[fieldName]
|
||||
Log.verbose("fieldName: \(fieldName), fieldValue: \(String(describing: fieldValue))")
|
||||
|
||||
switch type {
|
||||
/// Bool
|
||||
case is Bool.Type:
|
||||
return try decodeType(fieldValue?.boolean, to: T.self)
|
||||
/// Ints
|
||||
case is Int.Type:
|
||||
return try decodeType(fieldValue?.int, to: T.self)
|
||||
case is Int8.Type:
|
||||
return try decodeType(fieldValue?.int8, to: T.self)
|
||||
case is Int16.Type:
|
||||
return try decodeType(fieldValue?.int16, to: T.self)
|
||||
case is Int32.Type:
|
||||
return try decodeType(fieldValue?.int32, to: T.self)
|
||||
case is Int64.Type:
|
||||
return try decodeType(fieldValue?.int64, to: T.self)
|
||||
/// Int Arrays
|
||||
case is [Int].Type:
|
||||
return try decodeType(fieldValue?.intArray, to: T.self)
|
||||
case is [Int8].Type:
|
||||
return try decodeType(fieldValue?.int8Array, to: T.self)
|
||||
case is [Int16].Type:
|
||||
return try decodeType(fieldValue?.int16Array, to: T.self)
|
||||
case is [Int32].Type:
|
||||
return try decodeType(fieldValue?.int32Array, to: T.self)
|
||||
case is [Int64].Type:
|
||||
return try decodeType(fieldValue?.int64Array, to: T.self)
|
||||
/// UInts
|
||||
case is UInt.Type:
|
||||
return try decodeType(fieldValue?.uInt, to: T.self)
|
||||
case is UInt8.Type:
|
||||
return try decodeType(fieldValue?.uInt8, to: T.self)
|
||||
case is UInt16.Type:
|
||||
return try decodeType(fieldValue?.uInt16, to: T.self)
|
||||
case is UInt32.Type:
|
||||
return try decodeType(fieldValue?.uInt32, to: T.self)
|
||||
case is UInt64.Type:
|
||||
return try decodeType(fieldValue?.uInt64, to: T.self)
|
||||
/// UInt Arrays
|
||||
case is [UInt].Type:
|
||||
return try decodeType(fieldValue?.uIntArray, to: T.self)
|
||||
case is [UInt8].Type:
|
||||
return try decodeType(fieldValue?.uInt8Array, to: T.self)
|
||||
case is [UInt16].Type:
|
||||
return try decodeType(fieldValue?.uInt16Array, to: T.self)
|
||||
case is [UInt32].Type:
|
||||
return try decodeType(fieldValue?.uInt32Array, to: T.self)
|
||||
case is [UInt64].Type:
|
||||
return try decodeType(fieldValue?.uInt64Array, to: T.self)
|
||||
/// Floats
|
||||
case is Float.Type:
|
||||
return try decodeType(fieldValue?.float, to: T.self)
|
||||
case is [Float].Type:
|
||||
return try decodeType(fieldValue?.floatArray, to: T.self)
|
||||
/// Doubles
|
||||
case is Double.Type:
|
||||
return try decodeType(fieldValue?.double, to: T.self)
|
||||
case is [Double].Type:
|
||||
return try decodeType(fieldValue?.doubleArray, to: T.self)
|
||||
/// Dates
|
||||
case is Date.Type:
|
||||
switch dateDecodingStrategy {
|
||||
case .deferredToDate:
|
||||
guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)}
|
||||
return try decodeType(Date(timeIntervalSinceReferenceDate: (doubleValue)), to: T.self)
|
||||
case .secondsSince1970:
|
||||
guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)}
|
||||
return try decodeType(Date(timeIntervalSince1970: (doubleValue)), to: T.self)
|
||||
case .millisecondsSince1970:
|
||||
guard let doubleValue = fieldValue?.double else {return try decodeType(fieldValue, to: T.self)}
|
||||
return try decodeType(Date(timeIntervalSince1970: (doubleValue)), to: T.self)
|
||||
case .iso8601:
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
guard let stringValue = fieldValue?.string else {return try decodeType(fieldValue, to: T.self)}
|
||||
guard let date = _iso8601Formatter.date(from: stringValue) else {
|
||||
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "Expected date string to be ISO8601-formatted."))
|
||||
}
|
||||
return try decodeType(date, to: T.self)
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
case .formatted(let formatted):
|
||||
return try decodeType(fieldValue?.date(formatted), to: T.self)
|
||||
case .custom(let closure):
|
||||
return try decodeType(closure(self), to: T.self)
|
||||
#if swift(>=5) && !os(Linux)
|
||||
@unknown default:
|
||||
throw DateError.unknownStrategy
|
||||
#endif
|
||||
}
|
||||
case is [Date].Type:
|
||||
switch dateDecodingStrategy {
|
||||
case .deferredToDate:
|
||||
return try decodeType(fieldValue?.dateArray(decoderStrategy: .deferredToDate), to: T.self)
|
||||
case .secondsSince1970:
|
||||
return try decodeType(fieldValue?.dateArray(decoderStrategy: .secondsSince1970), to: T.self)
|
||||
case .millisecondsSince1970:
|
||||
return try decodeType(fieldValue?.dateArray(decoderStrategy: .millisecondsSince1970), to: T.self)
|
||||
case .iso8601:
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
return try decodeType(fieldValue?.dateArray(decoderStrategy: .iso8601), to: T.self)
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
case .formatted(let formatter):
|
||||
return try decodeType(fieldValue?.dateArray(formatter), to: T.self)
|
||||
case .custom(let closure):
|
||||
return try decodeType(fieldValue?.dateArray(decoderStrategy: .custom(closure), decoder: self), to: T.self)
|
||||
#if swift(>=5) && !os(Linux)
|
||||
@unknown default:
|
||||
throw DateError.unknownStrategy
|
||||
#endif
|
||||
}
|
||||
/// Strings
|
||||
case is String.Type:
|
||||
return try decodeType(fieldValue?.string, to: T.self)
|
||||
case is [String].Type:
|
||||
return try decodeType(fieldValue?.stringArray, to: T.self)
|
||||
case is Operation.Type:
|
||||
if let oType = type as? Operation.Type,
|
||||
let value = fieldValue?.string {
|
||||
let result = try oType.init(string: value)
|
||||
if let castedValue = result as? T {
|
||||
return castedValue
|
||||
}
|
||||
}
|
||||
return try decodeType(fieldValue?.decodable(T.self), to: T.self)
|
||||
case is Ordering.Type:
|
||||
if let oType = type as? Ordering.Type,
|
||||
let value = fieldValue?.string {
|
||||
let result = try oType.init(string: value)
|
||||
if let castedValue = result as? T {
|
||||
return castedValue
|
||||
}
|
||||
}
|
||||
return try decodeType(fieldValue?.decodable(T.self), to: T.self)
|
||||
case is Pagination.Type:
|
||||
if let oType = type as? Pagination.Type,
|
||||
let value = fieldValue?.string {
|
||||
let result = try oType.init(string: value)
|
||||
if let castedValue = result as? T {
|
||||
return castedValue
|
||||
}
|
||||
}
|
||||
return try decodeType(fieldValue?.decodable(T.self), to: T.self)
|
||||
default:
|
||||
Log.verbose("Decoding Custom Type: \(T.Type.self)")
|
||||
if fieldName.isEmpty {
|
||||
return try T(from: self)
|
||||
} else {
|
||||
// Processing an instance member of the class/struct
|
||||
return try decodeType(fieldValue?.decodable(T.self), to: T.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a keyed decoding container based on the key type.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
decoder.container(keyedBy: keyType)
|
||||
````
|
||||
*/
|
||||
public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
|
||||
return KeyedDecodingContainer(KeyedContainer<Key>(decoder: self))
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an unkeyed decoding container.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
decoder.unkeyedContainer()
|
||||
````
|
||||
*/
|
||||
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
return UnkeyedContainer(decoder: self)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a single value decoding container based on the key type.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
decoder.singleValueContainer()
|
||||
````
|
||||
*/
|
||||
public func singleValueContainer() throws -> SingleValueDecodingContainer {
|
||||
return UnkeyedContainer(decoder: self)
|
||||
}
|
||||
|
||||
private func decodeType<S: Decodable, T: Decodable>(_ object: S, to type: T.Type) throws -> T {
|
||||
if let values = object as? T {
|
||||
return values
|
||||
} else {
|
||||
throw decodingError()
|
||||
}
|
||||
}
|
||||
|
||||
private func decodingError() -> DecodingError {
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
let errorMsg = "Could not process field named '\(fieldName)'."
|
||||
Log.error(errorMsg)
|
||||
let errorCtx = DecodingError.Context(codingPath: codingPath, debugDescription: errorMsg)
|
||||
return DecodingError.dataCorrupted(errorCtx)
|
||||
}
|
||||
|
||||
private struct KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
|
||||
var decoder: QueryDecoder
|
||||
|
||||
var codingPath: [CodingKey] { return [] }
|
||||
|
||||
var allKeys: [Key] { return [] }
|
||||
|
||||
func contains(_ key: Key) -> Bool {
|
||||
return decoder.dictionary[key.stringValue] != nil
|
||||
}
|
||||
|
||||
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
|
||||
self.decoder.codingPath.append(key)
|
||||
defer { self.decoder.codingPath.removeLast() }
|
||||
return try decoder.decode(T.self)
|
||||
}
|
||||
|
||||
// If it is not in the dictionary or it is a empty string it should be nil
|
||||
func decodeNil(forKey key: Key) throws -> Bool {
|
||||
return decoder.dictionary[key.stringValue]?.isEmpty ?? true
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
return try decoder.container(keyedBy: type)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
|
||||
return try decoder.unkeyedContainer()
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
return decoder
|
||||
}
|
||||
|
||||
func superDecoder(forKey key: Key) throws -> Decoder {
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
|
||||
private struct UnkeyedContainer: UnkeyedDecodingContainer, SingleValueDecodingContainer {
|
||||
var decoder: QueryDecoder
|
||||
|
||||
var codingPath: [CodingKey] { return [] }
|
||||
|
||||
var count: Int? { return nil }
|
||||
|
||||
var currentIndex: Int { return 0 }
|
||||
|
||||
var isAtEnd: Bool { return false }
|
||||
|
||||
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
|
||||
return try decoder.decode(type)
|
||||
}
|
||||
|
||||
func decodeNil() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
return try decoder.container(keyedBy: type)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
|
||||
return self
|
||||
}
|
||||
|
||||
func superDecoder() throws -> Decoder {
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
522
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift
generated
Normal file
522
Pods/KituraContracts/Sources/KituraContracts/CodableQuery/QueryEncoder.swift
generated
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright IBM Corporation 2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import LoggerAPI
|
||||
|
||||
extension CharacterSet {
|
||||
static let customURLQueryAllowed = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~=:&")
|
||||
}
|
||||
|
||||
/**
|
||||
Query Parameter Encoder.
|
||||
|
||||
Encodes an `Encodable` object to a query parameter string, a `URLQueryItemArray`, or to a `[String: String]` dictionary. The encode function takes the `Encodable` object to encode as the parameter.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let date = Coder().dateFormatter.date(from: "2017-10-31T16:15:56+0000")
|
||||
let query = MyQuery(intField: -1, optionalIntField: 282, stringField: "a string", intArray: [1, -1, 3], dateField: date, optionalDateField: date, nested: Nested(nestedIntField: 333, nestedStringField: "nested string"))
|
||||
|
||||
guard let myQueryDict: [String: String] = try? QueryEncoder().encode(query) else {
|
||||
print("Failed to encode query to [String: String]")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public class QueryEncoder: Coder, Encoder, BodyEncoder {
|
||||
|
||||
/**
|
||||
A `[String: String]` dictionary.
|
||||
*/
|
||||
internal var dictionary: [String: String]
|
||||
|
||||
internal var anyDictionary: [String: Any]
|
||||
|
||||
/**
|
||||
The coding key path.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
````
|
||||
*/
|
||||
public var codingPath: [CodingKey] = []
|
||||
|
||||
/**
|
||||
The coding user info key.
|
||||
*/
|
||||
public var userInfo: [CodingUserInfoKey: Any] = [:]
|
||||
|
||||
// A `JSONDecoder.DateEncodingStrategy` date encoder used to determine what strategy
|
||||
// to use when encoding the specific date.
|
||||
private var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy
|
||||
|
||||
/**
|
||||
Initializer for the dictionary, which initializes an empty `[String: String]` dictionary.
|
||||
*/
|
||||
public override init() {
|
||||
self.dateEncodingStrategy = .formatted(Coder.defaultDateFormatter)
|
||||
self.dictionary = [:]
|
||||
self.anyDictionary = [:]
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Encodes an Encodable object to a query parameter string.
|
||||
|
||||
- Parameter value: The Encodable object to encode to its String representation.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let myQueryStr: String = try? QueryEncoder().encode(query) else {
|
||||
print("Failed to encode query to String")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func encode<T: Encodable>(_ value: T) throws -> String {
|
||||
let dict: [String : String] = try encode(value)
|
||||
let desc: String = dict.map { key, value in "\(key)=\(value)" }
|
||||
.reduce("") {pair1, pair2 in "\(pair1)&\(pair2)"}
|
||||
.addingPercentEncoding(withAllowedCharacters: CharacterSet.customURLQueryAllowed)!
|
||||
return "?" + String(desc.dropFirst())
|
||||
}
|
||||
|
||||
/**
|
||||
Encodes an Encodable object to Data.
|
||||
|
||||
- Parameter value: The Encodable object to encode to its Data representation.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let myQueryStr: Data = try? QueryEncoder().encode(query) else {
|
||||
print("Failed to encode query to Data")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func encode<T : Encodable>(_ value: T) throws -> Data {
|
||||
let dict: [String : String] = try encode(value)
|
||||
let desc: String? = dict.map { key, value in "\(key)=\(value)" }
|
||||
.reduce("") {pair1, pair2 in "\(pair1)&\(pair2)"}
|
||||
.addingPercentEncoding(withAllowedCharacters: CharacterSet.customURLQueryAllowed)
|
||||
guard let data = desc?.data(using: .utf8) else {
|
||||
throw RequestError.unprocessableEntity
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
Encodes an Encodable object to a URLQueryItem array.
|
||||
|
||||
- Parameter value: The Encodable object to encode to its [URLQueryItem] representation.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let myQueryArray: [URLQueryItem] = try? QueryEncoder().encode(query) else {
|
||||
print("Failed to encode query to [URLQueryItem]")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func encode<T: Encodable>(_ value: T) throws -> [URLQueryItem] {
|
||||
if let Q = T.self as? QueryParams.Type {
|
||||
dateEncodingStrategy = Q.dateEncodingStrategy
|
||||
}
|
||||
let dict: [String : String] = try encode(value)
|
||||
return dict.reduce([URLQueryItem]()) { array, element in
|
||||
var array = array
|
||||
array.append(URLQueryItem(name: element.key, value: element.value))
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Encodes an Encodable object to a `[String: String]` dictionary.
|
||||
|
||||
- Parameter value: The Encodable object to encode to its `[String: String]` representation.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
guard let myQueryDict: [String: String] = try? QueryEncoder().encode(query) else {
|
||||
print("Failed to encode query to [String: String]")
|
||||
return
|
||||
}
|
||||
````
|
||||
*/
|
||||
public func encode<T: Encodable>(_ value: T) throws -> [String : String] {
|
||||
let encoder = QueryEncoder()
|
||||
encoder.dateEncodingStrategy = self.dateEncodingStrategy
|
||||
if let Q = T.self as? QueryParams.Type {
|
||||
encoder.dateEncodingStrategy = Q.dateEncodingStrategy
|
||||
}
|
||||
try value.encode(to: encoder)
|
||||
return encoder.dictionary
|
||||
}
|
||||
|
||||
/// Encodes an Encodable object to a String -> String dictionary
|
||||
///
|
||||
/// - Parameter _ value: The Encodable object to encode to its [String: String] representation
|
||||
public func encode<T: Encodable>(_ value: T) throws -> [String : Any] {
|
||||
let encoder = QueryEncoder()
|
||||
encoder.dateEncodingStrategy = self.dateEncodingStrategy
|
||||
if let Q = T.self as? QueryParams.Type {
|
||||
encoder.dateEncodingStrategy = Q.dateEncodingStrategy
|
||||
}
|
||||
try value.encode(to: encoder)
|
||||
return encoder.anyDictionary
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a keyed encoding container based on the key type.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
encoder.container(keyedBy: keyType)
|
||||
````
|
||||
*/
|
||||
|
||||
public func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
|
||||
return KeyedEncodingContainer(KeyedContainer<Key>(encoder: self))
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an unkeyed encoding container.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
encoder.unkeyedContainer()
|
||||
````
|
||||
*/
|
||||
public func unkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
return UnkeyedContainer(encoder: self)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns an single value encoding container based on the key type.
|
||||
|
||||
### Usage Example: ###
|
||||
````swift
|
||||
encoder.singleValueContainer()
|
||||
````
|
||||
*/
|
||||
public func singleValueContainer() -> SingleValueEncodingContainer {
|
||||
return UnkeyedContainer(encoder: self)
|
||||
}
|
||||
|
||||
/// Decode a value for the current field, determined by this encoder's state (codingPath). Some
|
||||
/// paths through this function are recursive (for handling custom Date encodings).
|
||||
///
|
||||
/// Both the keyed and unkeyed containers call this function. The keyed container first sets the
|
||||
/// encoder's codingPath, which determines the field name we encode.
|
||||
///
|
||||
/// If a custom encoding is defined for Date, the custom closure will call this encoder back. It
|
||||
/// is expected that any such custom encoding produces a single value, calling back via the
|
||||
/// unkeyed container.
|
||||
///
|
||||
/// When custom encoding Date arrays, this function will be invoked multiple times for the same
|
||||
/// key. The =+= operator is used to build a comma-separated list of values for a key.
|
||||
internal func _encode<T: Encodable>(value: T) throws {
|
||||
let encoder = self
|
||||
let fieldName = Coder.getFieldName(from: encoder.codingPath)
|
||||
|
||||
switch value {
|
||||
/// Ints
|
||||
case let fieldValue as Int:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Int8:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Int16:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Int32:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Int64:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as [Int]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Int8]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Int16]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Int32]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Int64]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// UInts
|
||||
case let fieldValue as UInt:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as UInt8:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as UInt16:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as UInt32:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as UInt64:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
/// UInt Arrays
|
||||
case let fieldValue as [UInt]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Int Arrays
|
||||
case let fieldValue as [UInt8]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [UInt16]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [UInt32]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [UInt64]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Floats
|
||||
case let fieldValue as Float:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Float]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Doubles
|
||||
case let fieldValue as Double:
|
||||
encoder.dictionary[fieldName] =+= String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Double]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Bools
|
||||
case let fieldValue as Bool:
|
||||
encoder.dictionary[fieldName] = String(fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [Bool]:
|
||||
let strs: [String] = fieldValue.map { String($0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Strings
|
||||
case let fieldValue as String:
|
||||
encoder.dictionary[fieldName] =+= fieldValue
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as [String]:
|
||||
encoder.dictionary[fieldName] = fieldValue.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
/// Dates
|
||||
case let fieldValue as Date:
|
||||
switch encoder.dateEncodingStrategy {
|
||||
case .formatted(let formatter):
|
||||
encoder.dictionary[fieldName] = formatter.string(from: fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .deferredToDate:
|
||||
let date = NSNumber(value: fieldValue.timeIntervalSinceReferenceDate)
|
||||
encoder.dictionary[fieldName] = date.stringValue
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .secondsSince1970:
|
||||
let date = NSNumber(value: fieldValue.timeIntervalSince1970)
|
||||
encoder.dictionary[fieldName] = date.stringValue
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .millisecondsSince1970:
|
||||
let date = NSNumber(value: 1000 * fieldValue.timeIntervalSince1970)
|
||||
encoder.dictionary[fieldName] = date.stringValue
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .iso8601:
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
encoder.dictionary[fieldName] = _iso8601Formatter.string(from: fieldValue)
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
case .custom(let closure):
|
||||
try closure(fieldValue, encoder)
|
||||
#if swift(>=5) && !os(Linux)
|
||||
@unknown default:
|
||||
throw DateError.unknownStrategy
|
||||
#endif
|
||||
}
|
||||
case let fieldValue as [Date]:
|
||||
switch encoder.dateEncodingStrategy {
|
||||
case .deferredToDate:
|
||||
let dbs: [NSNumber] = fieldValue.map { NSNumber(value: $0.timeIntervalSinceReferenceDate) }
|
||||
let strs: [String] = dbs.map { ($0).stringValue}
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .secondsSince1970:
|
||||
let dbs: [NSNumber] = fieldValue.map { NSNumber(value: $0.timeIntervalSince1970) }
|
||||
let strs: [String] = dbs.map { ($0).stringValue}
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .millisecondsSince1970:
|
||||
let dbs: [NSNumber] = fieldValue.map { NSNumber(value: ($0.timeIntervalSince1970)/1000) }
|
||||
let strs: [String] = dbs.map { ($0).stringValue}
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case .iso8601:
|
||||
if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
|
||||
let strs: [String] = fieldValue.map { _iso8601Formatter.string(from: $0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
} else {
|
||||
fatalError("ISO8601DateFormatter is unavailable on this platform.")
|
||||
}
|
||||
case .formatted(let formatter):
|
||||
let strs: [String] = fieldValue.map { formatter.string(from: $0) }
|
||||
encoder.dictionary[fieldName] = strs.joined(separator: ",")
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
// This calls us back with each serialized element individually, with the same fieldName key,
|
||||
// which builds a comma-separated list using the '=+=' operator.
|
||||
case .custom(let closure):
|
||||
for element in fieldValue {
|
||||
try closure(element, encoder)
|
||||
}
|
||||
#if swift(>=5) && !os(Linux)
|
||||
@unknown default:
|
||||
throw DateError.unknownStrategy
|
||||
#endif
|
||||
}
|
||||
case let fieldValue as Operation:
|
||||
encoder.dictionary[fieldName] = fieldValue.getStringValue()
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Ordering:
|
||||
encoder.dictionary[fieldName] = fieldValue.getStringValue()
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
case let fieldValue as Pagination:
|
||||
encoder.dictionary[fieldName] = fieldValue.getStringValue()
|
||||
encoder.anyDictionary[fieldName] = fieldValue
|
||||
default:
|
||||
if fieldName.isEmpty {
|
||||
encoder.dictionary = [:] // Make encoder instance reusable
|
||||
encoder.anyDictionary = [:] // Make encoder instance reusable
|
||||
try value.encode(to: encoder)
|
||||
} else {
|
||||
do {
|
||||
let jsonData = try JSONEncoder().encode(value)
|
||||
encoder.dictionary[fieldName] = String(data: jsonData, encoding: .utf8)
|
||||
encoder.anyDictionary[fieldName] = jsonData
|
||||
} catch let error {
|
||||
throw encoder.encodingError(value, underlyingError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func encodingError(_ value: Any, underlyingError: Swift.Error?) -> EncodingError {
|
||||
let fieldName = Coder.getFieldName(from: codingPath)
|
||||
let errorCtx = EncodingError.Context(codingPath: codingPath, debugDescription: "Could not process field named '\(fieldName)'.", underlyingError: underlyingError)
|
||||
return EncodingError.invalidValue(value, errorCtx)
|
||||
}
|
||||
|
||||
private struct KeyedContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
|
||||
var encoder: QueryEncoder
|
||||
var codingPath: [CodingKey] { return [] }
|
||||
|
||||
/// The typical path for encoding a QueryParams (keyed) type. This encode will be called
|
||||
/// for each field in turn.
|
||||
func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
|
||||
self.encoder.codingPath.append(key)
|
||||
defer { self.encoder.codingPath.removeLast() }
|
||||
try encoder._encode(value: value)
|
||||
}
|
||||
|
||||
func encodeNil(forKey: Key) throws {}
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
return encoder.container(keyedBy: keyType)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
||||
return encoder.unkeyedContainer()
|
||||
}
|
||||
|
||||
func superEncoder() -> Encoder {
|
||||
return encoder
|
||||
}
|
||||
|
||||
func superEncoder(forKey key: Key) -> Encoder {
|
||||
return encoder
|
||||
}
|
||||
}
|
||||
|
||||
private struct UnkeyedContainer: UnkeyedEncodingContainer, SingleValueEncodingContainer {
|
||||
var encoder: QueryEncoder
|
||||
|
||||
var codingPath: [CodingKey] { return [] }
|
||||
|
||||
var count: Int { return 0 }
|
||||
|
||||
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
|
||||
return encoder.container(keyedBy: keyType)
|
||||
}
|
||||
|
||||
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
||||
return self
|
||||
}
|
||||
|
||||
func superEncoder() -> Encoder {
|
||||
return encoder
|
||||
}
|
||||
|
||||
func encodeNil() throws {}
|
||||
|
||||
/// This unkeyed encode will be called by a custom Date encoder. The correct key (field
|
||||
/// name) will already have been set by a call to the KeyedEncodingContainer.
|
||||
func encode<T>(_ value: T) throws where T : Encodable {
|
||||
try encoder._encode(value: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The '=+=' operator builds a comma-separated list of values for a given fieldName when encoding a [Date] that uses a custom formatting.
|
||||
infix operator =+=
|
||||
func =+= (lhs: inout String?, rhs: String) {
|
||||
if let lhsValue = lhs {
|
||||
lhs = lhsValue + "," + rhs
|
||||
} else {
|
||||
lhs = rhs
|
||||
}
|
||||
}
|
||||
|
||||
1550
Pods/KituraContracts/Sources/KituraContracts/Contracts.swift
generated
Normal file
1550
Pods/KituraContracts/Sources/KituraContracts/Contracts.swift
generated
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user