1 /**
2  An alias to `Result<Any, Exception>` which can be passed to the function callback.
3  */
4 public typealias FunctionCallResult = Result<Any, Exception>
5 
6 /**
7  A protocol for any type-erased function.
8  */
9 internal protocol AnyFunction: AnyDefinition, JavaScriptObjectBuilder {
10   /**
11    Name of the function. JavaScript refers to the function by this name.
12    */
13   var name: String { get }
14 
15   /**
16    An array of the dynamic types that the function takes. If the last type is `Promise`, it's not included.
17    */
18   var dynamicArgumentTypes: [AnyDynamicType] { get }
19 
20   /**
21    A number of arguments the function takes. If the function expects to receive an owner (`this`) as the first argument, it's not counted.
22    Similarly, if the last argument is of type `Promise`, it is not counted.
23    */
24   var argumentsCount: Int { get }
25 
26   /**
27    A minimum number of arguments the functions needs which equals to `argumentsCount` reduced by the number of trailing optional arguments.
28    */
29   var requiredArgumentsCount: Int { get }
30 
31   /**
32    Indicates whether the function's arguments starts from the owner that calls this function.
33    */
34   var takesOwner: Bool { get set }
35 
36   /**
37    Calls the function with a given owner and arguments and returns a result through the callback block.
38    - Parameters:
39       - owner: An object that calls this function. If the `takesOwner` property is true
40       and type of the first argument matches the owner type, it's being passed as the argument.
41       - args: An array of arguments to pass to the function. They could be Swift primitives
42       when invoked through the bridge and in unit tests or `JavaScriptValue`s
43       when the function is called through the JSI.
44       - appContext: An app context where the function is being executed.
45       - callback: A callback that receives a result of the function execution.
46    */
47   func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> ())
48 }
49 
50 extension AnyFunction {
51   var requiredArgumentsCount: Int {
52     var trailingOptionalArgumentsCount: Int = 0
53 
54     for dynamicArgumentType in dynamicArgumentTypes.reversed() {
55       if dynamicArgumentType is DynamicOptionalType {
56         trailingOptionalArgumentsCount += 1
57       } else {
58         break
59       }
60     }
61     return argumentsCount - trailingOptionalArgumentsCount
62   }
63 
64   var argumentsCount: Int {
65     return dynamicArgumentTypes.count
66   }
67 
68   /**
69    Calls the function just like `call(by:withArguments:appContext:callback:)`, but without an owner
70    and with an empty callback. Might be useful when you only want to call the function,
71    but don't care about the result.
72    */
73   func call(withArguments args: [Any], appContext: AppContext) {
74     call(by: nil, withArguments: args, appContext: appContext, callback: { _ in })
75   }
76 }
77 
78 internal class FunctionCallException: GenericException<String> {
79   override var reason: String {
80     "Calling the '\(param)' function has failed"
81   }
82 }
83