Last active
April 13, 2021 20:54
-
-
Save NSExceptional/25a9f35a706019940783bd2661939abd 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
import Cocoa | |
import Foundation | |
class Person { | |
var age: Int | |
var name: String | |
init(age: Int, name: String) { | |
self.age = age | |
self.name = name | |
} | |
} | |
enum TransformError: Error { | |
case notConvertible | |
} | |
enum JSON { | |
case null | |
case bool(Bool) | |
case int(Int) | |
case float(Double) | |
case string(String) | |
case array([JSONCodable]) | |
case object([String: JSONCodable]) | |
var toBool: Bool { | |
switch self { | |
case .null: return false | |
case .bool(let v): return v | |
case .int(let v): return v != 0 | |
case .float(let v): return v != 0.0 | |
case .string(let v): return !v.isEmpty | |
case .array(let v): return !v.isEmpty | |
case .object(_): return true | |
} | |
} | |
var toString: String { | |
switch self { | |
case .null: return "null" | |
case .bool(let v): return String(v) | |
case .int(let v): return String(v) | |
case .float(let v): return String(v) | |
case .string(let v): return v | |
case .array(let v): | |
let data = try! JSONSerialization.data(withJSONObject: v, options: []) | |
return String(data: data, encoding: .utf8)! | |
case .object(let v): | |
let data = try! JSONSerialization.data(withJSONObject: v, options: []) | |
return String(data: data, encoding: .utf8)! | |
} | |
} | |
var toInt: Int? { | |
switch self { | |
case .null: return 0 | |
case .bool(let v): return v ? 1 : 0 | |
case .int(let v): return v | |
case .float(let v): return Int(v) | |
case .string(let v): return Int(v) ?? nil | |
case .array(_): return nil | |
case .object(_): return nil | |
} | |
} | |
var toFloat: Double { | |
return 3.14 | |
} | |
var toArray: [JSON] { | |
switch self { | |
case .null: return [] | |
case .object(let o): return Array(o.values) | |
default: return [] | |
} | |
} | |
var toObject: [String: JSONCodable] { | |
if case let .object(v) = self { | |
return v | |
} | |
fatalError("Cannot convert non-objects to object") | |
} | |
} | |
protocol JSONCodable { | |
var toJSON: JSON { get } | |
static var defaultJSON: JSON { get } | |
static func decode(from json: JSON) throws | |
} | |
struct Transformer<T: JSONCodable, U: JSONCodable> { | |
static func transform(_ t: T?) throws -> U { | |
return try U(from: t?.toJSON ?? T.defaultJSON) | |
} | |
static func reverse(_ u: U?) throws -> T { | |
return try T(from: u?.toJSON ?? U.defaultJSON) | |
} | |
func transform(_ t: T?) throws -> U { | |
return try type(of: self).transform(t) | |
} | |
func reverse(_ u: U?) throws -> T { | |
return try type(of: self).reverse(u) | |
} | |
} | |
extension Optional: JSONCodable where Wrapped: JSONCodable { | |
var toJSON: JSON { | |
switch self { | |
case .none: return .null | |
case .some(let v): return v.toJSON | |
} | |
} | |
static var defaultJSON: JSON { | |
return .null | |
} | |
static func decode(from json: JSON) throws { | |
switch json { | |
case .null: return nil | |
case .bool(let v): return v as? Wrapped | |
case .int(let v): return v as? Wrapped | |
case .float(let v): return v as? Wrapped | |
case .string(let v): return v as? Wrapped | |
case .array(let v): return v as? Wrapped | |
case .object(let v): return v as? Wrapped | |
} | |
} | |
} | |
extension Bool: JSONCodable { | |
var toJSON: JSON { | |
return .bool(self) | |
} | |
static var defaultJSON: JSON { | |
return .bool(false) | |
} | |
static func decode(from json: JSON) throws { | |
return json.toBool | |
} | |
} | |
extension String: JSONCodable { | |
var toJSON: JSON { | |
return .string(self) | |
} | |
static var defaultJSON: JSON { | |
return .string("") | |
} | |
static func decode(from json: JSON) throws { | |
return json.toString | |
} | |
} | |
extension Int: JSONCodable { | |
var toJSON: JSON { | |
return .int(self) | |
} | |
static var defaultJSON: JSON { | |
return .int(0) | |
} | |
static func decode(from json: JSON) throws { | |
guard let value = json.toInt else { | |
throw TransformError.notConvertible | |
} | |
return value | |
} | |
} | |
let maybe: Bool? = nil | |
let number = "3456" | |
let notNumber = "boobies" | |
// Coerce Bool? to Bool before transform; no null | |
try! Transformer<Bool,String>.transform(maybe) | |
// Explicitly allow null | |
try! Transformer<Bool?,String>().transform(maybe) | |
// Force conversion from String to Int | |
try! Transformer<String,Int>.transform(number) | |
// Conversion from String to optional Int | |
try! Transformer<String,Int?>().transform(notNumber) | |
// Failed forced conversion | |
try? Transformer<String,Int>.transform(notNumber) | |
func reflectAllPropsToJSON<T>(_ t: T) -> [String: JSONCodable] { | |
return ["fake": "object"] | |
} | |
func decodeObject<T>(from json: JSON) throws -> T { | |
} | |
extension Person: JSONCodable { | |
static var defaultJSON: JSON { | |
return .null | |
} | |
var toJSON: JSON { | |
return .object(reflectAllPropsToJSON(self)) | |
} | |
static func decode(from json: JSON) throws { | |
return try decodeObject(from: json) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment