Skip to content

Instantly share code, notes, and snippets.

@NSExceptional
Last active April 13, 2021 20:54
Show Gist options
  • Save NSExceptional/25a9f35a706019940783bd2661939abd to your computer and use it in GitHub Desktop.
Save NSExceptional/25a9f35a706019940783bd2661939abd to your computer and use it in GitHub Desktop.
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