1ec62a260STomasz Sapeta // Copyright 2022-present 650 Industries. All rights reserved. 2ec62a260STomasz Sapeta 3ec62a260STomasz Sapeta /** 4ec62a260STomasz Sapeta Enum with available kinds of values. It's almost the same as a result of "typeof" 5ec62a260STomasz Sapeta in JavaScript, however `null` has its own kind (typeof null == "object"). 6ec62a260STomasz Sapeta */ 7ec62a260STomasz Sapeta public enum JavaScriptValueKind: String { 8ec62a260STomasz Sapeta case undefined 9ec62a260STomasz Sapeta case null 10ec62a260STomasz Sapeta case bool 11ec62a260STomasz Sapeta case number 12ec62a260STomasz Sapeta case symbol 13ec62a260STomasz Sapeta case string 14ec62a260STomasz Sapeta case function 15ec62a260STomasz Sapeta case object 16ec62a260STomasz Sapeta } 17ec62a260STomasz Sapeta 18a2ddfabcSTomasz Sapeta /** 19a2ddfabcSTomasz Sapeta A protocol that JavaScript values, objects and functions can conform to. 20a2ddfabcSTomasz Sapeta */ 21a2ddfabcSTomasz Sapeta protocol AnyJavaScriptValue { 22a2ddfabcSTomasz Sapeta /** 23a2ddfabcSTomasz Sapeta Tries to convert a raw JavaScript value to the conforming type. 24a2ddfabcSTomasz Sapeta */ convertnull25a2ddfabcSTomasz Sapeta static func convert(from value: JavaScriptValue, appContext: AppContext) throws -> Self 26a2ddfabcSTomasz Sapeta } 27a2ddfabcSTomasz Sapeta 28*45a388e1SŁukasz Kosmaty extension JavaScriptValue: AnyJavaScriptValue, AnyArgument { 2972193c75SŁukasz Kosmaty public var kind: JavaScriptValueKind { 30ec62a260STomasz Sapeta switch true { 31ec62a260STomasz Sapeta case isUndefined(): 32ec62a260STomasz Sapeta return .undefined 33ec62a260STomasz Sapeta case isNull(): 34ec62a260STomasz Sapeta return .null 35ec62a260STomasz Sapeta case isBool(): 36ec62a260STomasz Sapeta return .bool 37ec62a260STomasz Sapeta case isNumber(): 38ec62a260STomasz Sapeta return .number 39ec62a260STomasz Sapeta case isSymbol(): 40ec62a260STomasz Sapeta return .symbol 41ec62a260STomasz Sapeta case isString(): 42ec62a260STomasz Sapeta return .string 43ec62a260STomasz Sapeta case isFunction(): 44ec62a260STomasz Sapeta return .function 45ec62a260STomasz Sapeta default: 46ec62a260STomasz Sapeta return .object 47ec62a260STomasz Sapeta } 48ec62a260STomasz Sapeta } 49ec62a260STomasz Sapeta 50ec62a260STomasz Sapeta func asBool() throws -> Bool { 51ec62a260STomasz Sapeta if isBool() { 52ec62a260STomasz Sapeta return getBool() 53ec62a260STomasz Sapeta } 54ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Bool")) 55ec62a260STomasz Sapeta } 56ec62a260STomasz Sapeta 57ec62a260STomasz Sapeta func asInt() throws -> Int { 58ec62a260STomasz Sapeta if isNumber() { 59ec62a260STomasz Sapeta return getInt() 60ec62a260STomasz Sapeta } 61ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Int")) 62ec62a260STomasz Sapeta } 63ec62a260STomasz Sapeta 64ec62a260STomasz Sapeta func asDouble() throws -> Double { 65ec62a260STomasz Sapeta if isNumber() { 66ec62a260STomasz Sapeta return getDouble() 67ec62a260STomasz Sapeta } 68ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Double")) 69ec62a260STomasz Sapeta } 70ec62a260STomasz Sapeta 71ec62a260STomasz Sapeta func asString() throws -> String { 72ec62a260STomasz Sapeta if isString() { 73ec62a260STomasz Sapeta return getString() 74ec62a260STomasz Sapeta } 75ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "String")) 76ec62a260STomasz Sapeta } 77ec62a260STomasz Sapeta 78ec62a260STomasz Sapeta func asArray() throws -> [JavaScriptValue?] { 79ec62a260STomasz Sapeta if isObject() { 80ec62a260STomasz Sapeta return getArray() 81ec62a260STomasz Sapeta } 82ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Array")) 83ec62a260STomasz Sapeta } 84ec62a260STomasz Sapeta 8556c10913STomasz Sapeta func asDict() throws -> [String: Any] { 86ec62a260STomasz Sapeta if isObject() { 87ec62a260STomasz Sapeta return getDictionary() 88ec62a260STomasz Sapeta } 89ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Dict")) 90ec62a260STomasz Sapeta } 91ec62a260STomasz Sapeta 92ec62a260STomasz Sapeta func asObject() throws -> JavaScriptObject { 93ec62a260STomasz Sapeta if isObject() { 94ec62a260STomasz Sapeta return getObject() 95ec62a260STomasz Sapeta } 96ec62a260STomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Object")) 97ec62a260STomasz Sapeta } 98002d516eSTomasz Sapeta 99a2ddfabcSTomasz Sapeta func asFunction() throws -> RawJavaScriptFunction { 100a2ddfabcSTomasz Sapeta if isFunction() { 101a2ddfabcSTomasz Sapeta return getFunction() 102a2ddfabcSTomasz Sapeta } 103a2ddfabcSTomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "Function")) 104a2ddfabcSTomasz Sapeta } 105a2ddfabcSTomasz Sapeta 106002d516eSTomasz Sapeta func asTypedArray() throws -> JavaScriptTypedArray { 107002d516eSTomasz Sapeta if let typedArray = getTypedArray() { 108002d516eSTomasz Sapeta return typedArray 109002d516eSTomasz Sapeta } 110002d516eSTomasz Sapeta throw JavaScriptValueConversionException((kind: kind, target: "TypedArray")) 111002d516eSTomasz Sapeta } 112a2ddfabcSTomasz Sapeta 113a2ddfabcSTomasz Sapeta // MARK: - AnyJavaScriptValue 114a2ddfabcSTomasz Sapeta 115a2ddfabcSTomasz Sapeta internal static func convert(from value: JavaScriptValue, appContext: AppContext) throws -> Self { 116a2ddfabcSTomasz Sapeta // It's already a `JavaScriptValue` so it should always pass through. 117a2ddfabcSTomasz Sapeta if let value = value as? Self { 118a2ddfabcSTomasz Sapeta return value 119a2ddfabcSTomasz Sapeta } 120a2ddfabcSTomasz Sapeta throw JavaScriptValueConversionException((kind: value.kind, target: String(describing: Self.self))) 121a2ddfabcSTomasz Sapeta } 122ec62a260STomasz Sapeta } 123ec62a260STomasz Sapeta 124ec62a260STomasz Sapeta internal final class JavaScriptValueConversionException: GenericException<(kind: JavaScriptValueKind, target: String)> { 125ec62a260STomasz Sapeta override var reason: String { 126ec62a260STomasz Sapeta "Cannot represent a value of kind '\(param.kind)' as \(param.target)" 127ec62a260STomasz Sapeta } 128ec62a260STomasz Sapeta } 129