Created
November 14, 2019 14:27
-
-
Save sveinhal/c2abd45a23d053a965e055231a26b3fc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// RandomDecoder.swift | |
// Utils | |
// | |
// Created by Svein Halvor Halvorsen on 24/10/2019. | |
import Foundation | |
protocol RandomInstantiable { | |
static func randomInstance<G: RandomNumberGenerator>(using: inout G) -> Self | |
} | |
extension RandomInstantiable { | |
static func randomInstance() -> Self { | |
var generator = SystemRandomNumberGenerator() | |
return self.randomInstance(using: &generator) | |
} | |
} | |
extension String: RandomInstantiable { | |
static func randomInstance<G>(using generator: inout G) -> String where G: RandomNumberGenerator { | |
return randomInstance(length: Int.random(in: 3..<20, using: &generator), using: &generator) | |
} | |
static func randomInstance<G>(length: Int, using generator: inout G) -> String where G: RandomNumberGenerator { | |
let source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" | |
return String((0..<length).compactMap({ _ in source.randomElement(using: &generator) })) | |
} | |
} | |
extension URL: RandomInstantiable { | |
static func randomInstance<G>(using generator: inout G) -> URL where G: RandomNumberGenerator { | |
var components = URLComponents() | |
//swiftlint:disable:next force_unwrapping | |
let tld = ["example.com", "example.org", "example.net"].randomElement(using: &generator)! | |
let host = Bool.random(using: &generator) ? String.randomInstance(using: &generator) : nil | |
components.host = [host, tld].compactMap({ $0 }).joined(separator: ".") | |
components.scheme = Bool.random(using: &generator) ? "http" : "https" | |
components.path = "/" + (0...Int.random(in: 0...2, using: &generator)) | |
.compactMap { _ in String.randomInstance(using: &generator) } | |
.joined(separator: "/") | |
//swiftlint:disable:next force_unwrapping | |
return components.url! | |
} | |
} | |
extension Date: RandomInstantiable { | |
static func randomInstance<G>(using generator: inout G) -> Date where G: RandomNumberGenerator { | |
Date(timeIntervalSinceNow: TimeInterval.random(in: -31_000_000...31_000_000, using: &generator)) | |
} | |
} | |
class RandomDecoder<G: RandomNumberGenerator>: Decoder { | |
var codingPath: [CodingKey] = [] | |
var userInfo: [CodingUserInfoKey: Any] = [:] | |
private var generator: G | |
public init(using generator: inout G) { | |
self.generator = generator | |
} | |
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey { | |
return KeyedDecodingContainer(KeyedContainer<Key>(using: &generator)) | |
} | |
func unkeyedContainer() throws -> UnkeyedDecodingContainer { | |
return UnkeyedContainer(using: &generator) | |
} | |
func singleValueContainer() throws -> SingleValueDecodingContainer { | |
return SingleValueContainer(using: &generator) | |
} | |
class UnkeyedContainer: UnkeyedDecodingContainer { | |
var generator: G | |
var codingPath: [CodingKey] = [] | |
var count: Int? = (0...5).randomElement() | |
var isAtEnd: Bool { currentIndex == (count ?? 0) } | |
private(set) var currentIndex: Int = 0 | |
public init(using generator: inout G) { | |
self.generator = generator | |
} | |
func decodeNil() throws -> Bool { .random(using: &generator) } | |
func decode<T>(_ type: T.Type) throws -> T where T: Decodable { | |
defer { currentIndex += 1 } | |
return try SingleValueContainer(using: &generator).decode(type) | |
} | |
func nestedContainer<NestedKey>( | |
keyedBy type: NestedKey.Type | |
) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey { | |
return KeyedDecodingContainer(KeyedContainer<NestedKey>(using: &generator)) | |
} | |
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { UnkeyedContainer(using: &generator) } | |
func superDecoder() throws -> Decoder { RandomDecoder(using: &generator) } | |
} | |
class KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol { | |
var generator: G | |
var codingPath: [CodingKey] = [] | |
var allKeys: [Key] = [] | |
public init(using generator: inout G) { | |
self.generator = generator | |
} | |
func contains(_ key: Key) -> Bool { return true } | |
func decodeNil(forKey key: Key) throws -> Bool { .random(using: &generator) } | |
func decode<T: Decodable>(_ type: T.Type, forKey key: Key) throws -> T { | |
try SingleValueContainer(using: &generator).decode(type) | |
} | |
func nestedContainer<NestedKey>( | |
keyedBy type: NestedKey.Type, forKey key: Key | |
) throws -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey { | |
return KeyedDecodingContainer(KeyedContainer<NestedKey>(using: &generator)) | |
} | |
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { UnkeyedContainer(using: &generator) } | |
func superDecoder() throws -> Decoder { RandomDecoder(using: &generator) } | |
func superDecoder(forKey key: Key) throws -> Decoder { RandomDecoder(using: &generator) } | |
} | |
class SingleValueContainer: SingleValueDecodingContainer { | |
var generator: G | |
var codingPath: [CodingKey] = [] | |
init(using generator: inout G) { | |
self.generator = generator | |
} | |
func decodeNil() -> Bool { .random(using: &generator) } | |
func decode(_ type: Bool.Type) throws -> Bool { .random(using: &generator) } | |
func decode(_ type: Double.Type) throws -> Double { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: Float.Type) throws -> Float { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: Int.Type) throws -> Int { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: Int8.Type) throws -> Int8 { .random(in: 0..<Int8.max, using: &generator) } | |
func decode(_ type: Int16.Type) throws -> Int16 { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: Int32.Type) throws -> Int32 { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: Int64.Type) throws -> Int64 { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: UInt.Type) throws -> UInt { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: UInt8.Type) throws -> UInt8 { .random(in: 0..<UInt8.max, using: &generator) } | |
func decode(_ type: UInt16.Type) throws -> UInt16 { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: UInt32.Type) throws -> UInt32 { .random(in: 0..<500, using: &generator) } | |
func decode(_ type: UInt64.Type) throws -> UInt64 { .random(in: 0..<500, using: &generator) } | |
func decode<T: Decodable>(_ type: T.Type) throws -> T { | |
if let type = type as? RandomInstantiable.Type { | |
//swiftlint:disable:next force_cast | |
return type.randomInstance() as! T | |
} else { | |
return try T(from: RandomDecoder(using: &generator)) | |
} | |
} | |
} | |
} | |
extension RandomDecoder where G == SystemRandomNumberGenerator { | |
convenience init() { | |
var generator = SystemRandomNumberGenerator() | |
self.init(using: &generator) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment