1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 // MARK: - Arguments
4 
5 /**
6  Tries to cast given argument to the type that is wrapped by the dynamic type.
7  - Parameters:
8   - argument: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value.
9   - argumentType: Something that implements ``AnyDynamicType`` and knows how to cast the argument.
10  - Returns: A new value converted according to the dynamic type.
11  - Throws: Rethrows various exceptions that could be thrown by the dynamic types.
12  */
13 internal func castArgument(_ argument: Any, toType argumentType: AnyDynamicType) throws -> Any {
14   // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
15   if argumentType is DynamicSharedObjectType {
16     return try argumentType.cast(argument)
17   }
18   if let argument = argument as? JavaScriptValue {
19     return try argumentType.cast(argument.getRaw())
20   }
21   return try argumentType.cast(argument)
22 }
23 
24 /**
25  Same as ``castArgument(_:argumentType:)`` but for an array of arguments.
26  - Parameters:
27    - arguments: An array of arguments to be cast.
28    - argumentTypes: An array of the dynamic types in the same order as the array of arguments.
29  - Returns: An array of arguments after casting. Its size is the same as the input arrays.
30  - Throws: ``InvalidArgsNumberException`` when the sizes of arrays passed as parameters are not equal.
31    Rethrows exceptions thrown by ``castArgument(_:argumentType:)``.
32  */
33 internal func castArguments(_ arguments: [Any], toTypes argumentTypes: [AnyDynamicType]) throws -> [Any] {
34   if arguments.count != argumentTypes.count {
35     throw InvalidArgsNumberException((received: arguments.count, expected: argumentTypes.count))
36   }
37   return try arguments.enumerated().map { index, argument in
38     let argumentType = argumentTypes[index]
39 
40     do {
41       return try castArgument(argument, toType: argumentType)
42     } catch {
43       throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
44     }
45   }
46 }
47 
48 // MARK: - Exceptions
49 
50 internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> {
51   override var reason: String {
52     "Received \(param.received) arguments, but \(param.expected) was expected"
53   }
54 }
55 
56 internal class ArgumentCastException: GenericException<(index: Int, type: AnyDynamicType)> {
57   override var reason: String {
58     "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)"
59   }
60 }
61 
62 private class ModuleUnavailableException: GenericException<String> {
63   override var reason: String {
64     "Module '\(param)' is no longer available"
65   }
66 }
67