import Foundation import VGSL extension [String: Function] { mutating func addArrayFunctions() { addFunction("getArrayFromArray", _getArray) addFunction("getOptArrayFromArray", _getOptArray) addFunction("getDictFromArray", _getDict) addFunction("getOptDictFromArray", _getOptDict) addFunctions("Boolean", _getBoolean) addFunctions("OptBoolean", _getOptBoolean) addFunctions("Color", _getColor) addFunctions("OptColor", _getOptColor) addFunctions("Integer", _getInteger) addFunctions("OptInteger", _getOptInteger) addFunctions("Number", _getNumber) addFunctions("OptNumber", _getOptNumber) addFunctions("String", _getString) addFunctions("OptString", _getOptString) addFunctions("Url", _getUrl) addFunctions("OptUrl", _getOptUrl) addFunction("len", FunctionUnary { $0.count }) } mutating func addArrayMethods() { addFunction("getArray", _getArray) addFunction("getBoolean", _getBoolean) addFunction("getColor", _getColor) addFunction("getDict", _getDict) addFunction("getInteger", _getInteger) addFunction("getNumber", _getNumber) addFunction("getString", _getString) addFunction("getUrl", _getUrl) addFunction("isEmpty", _isEmpty) } private mutating func addFunctions( _ typeName: String, _ function: Function ) { self["get\(typeName)FromArray"] = function self["getArray\(typeName)"] = function } } private let _getArray = FunctionBinary { try $0.getArray(index: $1) } private let _getBoolean = FunctionBinary { try $0.getBoolean(index: $1) } private let _getColor = FunctionBinary { try $0.getColor(index: $1) } private let _getDict = FunctionBinary { try $0.getDict(index: $1) } private let _getInteger = FunctionBinary { try $0.getInteger(index: $1) } private let _getNumber = FunctionBinary { try $0.getNumber(index: $1) } private let _getString = FunctionBinary { try $0.getString(index: $1) } private let _getUrl = FunctionBinary { try $0.getUrl(index: $1) } private let _isEmpty = FunctionUnary { $0.isEmpty } private let _getOptArray = FunctionBinary { (try? $0.getArray(index: $1)) ?? [] } private let _getOptBoolean = FunctionTernary { (try? $0.getBoolean(index: $1)) ?? $2 } private let _getOptColor = OverloadedFunction(functions: [ FunctionTernary { (try? $0.getColor(index: $1)) ?? $2 }, FunctionTernary { if let value = try? $0.getColor(index: $1) { return value } return Color.color(withHexString: $2)! }, ]) private let _getOptDict = FunctionBinary { (try? $0.getDict(index: $1)) ?? [:] } private let _getOptInteger = FunctionTernary { (try? $0.getInteger(index: $1)) ?? $2 } private let _getOptNumber = FunctionTernary { (try? $0.getNumber(index: $1)) ?? $2 } private let _getOptString = FunctionTernary { (try? $0.getString(index: $1)) ?? $2 } private let _getOptUrl = OverloadedFunction(functions: [ FunctionTernary { (try? $0.getUrl(index: $1)) ?? $2 }, FunctionTernary { if let value = try? $0.getUrl(index: $1) { return value } return URL(string: $2)! }, ]) extension DivArray { fileprivate func getArray(index: Int) throws -> DivArray { let value = try getValue(index: index) guard let arrayValue = value as? DivArray else { throw ExpressionError.incorrectType("Array", value) } return arrayValue } fileprivate func getDict(index: Int) throws -> DivDictionary { let value = try getValue(index: index) guard let dictValue = value as? DivDictionary else { throw ExpressionError.incorrectType("Dict", value) } return dictValue } fileprivate func getBoolean(index: Int) throws -> Bool { let value = try getValue(index: index) guard value.isBool, let boolValue = value as? Bool else { throw ExpressionError.incorrectType("Boolean", value) } return boolValue } fileprivate func getColor(index: Int) throws -> Color { let value = try getValue(index: index) guard let stringValue = value as? String else { throw ExpressionError.incorrectType("Color", value) } guard let color = Color.color(withHexString: stringValue) else { throw ExpressionError("Unable to convert value to Color, expected format #AARRGGBB.") } return color } fileprivate func getInteger(index: Int) throws -> Int { let value = try getValue(index: index) if value.isBool { throw ExpressionError.incorrectType("Integer", value) } guard let intValue = value as? Int else { if let doubleValue = value as? Double { if doubleValue < Double(Int.min) || doubleValue > Double(Int.max) { throw ExpressionError.integerOverflow() } throw ExpressionError("Cannot convert value to integer.") } throw ExpressionError.incorrectType("Integer", value) } return intValue } fileprivate func getNumber(index: Int) throws -> Double { let value = try getValue(index: index) if value.isBool { throw ExpressionError.incorrectType("Number", value) } if let numberValue = value as? Double { return numberValue } if let intValue = value as? Int { return Double(intValue) } throw ExpressionError.incorrectType("Number", value) } fileprivate func getString(index: Int) throws -> String { let value = try getValue(index: index) guard let stringValue = value as? String else { throw ExpressionError.incorrectType("String", value) } return stringValue } fileprivate func getUrl(index: Int) throws -> URL { let value = try getValue(index: index) guard let stringValue = value as? String, let url = URL(string: stringValue) else { throw ExpressionError.incorrectType("Url", value) } return url } private func getValue(index: Int) throws -> AnyHashable { if index >= 0, index < count { return self[index] } throw ExpressionError("Requested index (\(index)) out of bounds array size (\(count)).") } }