1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 // FIXME: Calling module's functions needs solid refactoring to not reference the module holder.
4 // Instead, it should be possible to directly call the function instance from here. (added by @tsapeta)
5 
6 /**
7  Creates a block that is executed when the module's async function is called.
8  */
9 internal func createAsyncFunctionBlock(holder: ModuleHolder, name functionName: String) -> JSAsyncFunctionBlock {
10   let moduleName = holder.name
11   return { [weak holder, moduleName] args, resolve, reject in
12     guard let holder = holder else {
13       let exception = ModuleUnavailableException(moduleName)
14       reject(exception.code, exception.description, exception)
15       return
16     }
17     holder.call(function: functionName, args: args) { result in
18       switch result {
19       case .failure(let error):
20         reject(error.code, error.description, nil)
21       case .success(let value):
22         resolve(value)
23       }
24     }
25   }
26 }
27 
28 /**
29  Creates a block that is executed when the module's sync function is called.
30  */
31 internal func createSyncFunctionBlock(holder: ModuleHolder, name functionName: String) -> JSSyncFunctionBlock {
32   return { [weak holder] args in
33     guard let holder = holder else {
34       return nil
35     }
36     return holder.callSync(function: functionName, args: args)
37   }
38 }
39 
40 // MARK: - Arguments
41 
42 /**
43  Tries to cast given argument to the type that is wrapped by the argument type.
44  - Parameters:
45   - argument: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value.
46   - argumentType: Something that implements ``AnyArgumentType`` and knows how to cast the argument.
47  - Returns: A new value converted according to the argument type.
48  - Throws: Rethrows various exceptions that could be thrown by the argument type wrappers.
49  */
50 internal func castArgument(_ argument: Any, toType argumentType: AnyArgumentType) throws -> Any {
51   // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
52   if let argument = argument as? JavaScriptValue {
53     return try argumentType.cast(argument.getRaw())
54   }
55   return try argumentType.cast(argument)
56 }
57 
58 /**
59  Same as ``castArgument(_:argumentType:)`` but for an array of arguments.
60  - Parameters:
61    - arguments: An array of arguments to be cast.
62    - argumentTypes: An array of argument types in the same order as the array of arguments.
63  - Returns: An array of arguments after casting. Its size is the same as the input arrays.
64  - Throws: ``InvalidArgsNumberException`` when the sizes of arrays passed as parameters are not equal.
65    Rethrows exceptions thrown by ``castArgument(_:argumentType:)``.
66  */
67 internal func castArguments(_ arguments: [Any], toTypes argumentTypes: [AnyArgumentType]) throws -> [Any] {
68   if arguments.count != argumentTypes.count {
69     throw InvalidArgsNumberException((received: arguments.count, expected: argumentTypes.count))
70   }
71   return try arguments.enumerated().map { index, argument in
72     let argumentType = argumentTypes[index]
73 
74     do {
75       return try castArgument(argument, toType: argumentType)
76     } catch {
77       throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
78     }
79   }
80 }
81 
82 // MARK: - Exceptions
83 
84 internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> {
85   override var reason: String {
86     "Received \(param.received) arguments, but \(param.expected) was expected"
87   }
88 }
89 
90 internal class ArgumentCastException: GenericException<(index: Int, type: AnyArgumentType)> {
91   override var reason: String {
92     "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)"
93   }
94 }
95 
96 private class ModuleUnavailableException: GenericException<String> {
97   override var reason: String {
98     "Module '\(param)' is no longer available"
99   }
100 }
101