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