Skip to content

Instantly share code, notes, and snippets.

@sveinhal
Created November 14, 2019 14:27
Show Gist options
  • Save sveinhal/c2abd45a23d053a965e055231a26b3fc to your computer and use it in GitHub Desktop.
Save sveinhal/c2abd45a23d053a965e055231a26b3fc to your computer and use it in GitHub Desktop.
// 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