expo-modules-core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/android/ExpoModulesCorePlugin.gradle +15 -0
  4. package/android/build.gradle +46 -32
  5. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +5 -5
  6. package/android/src/main/java/expo/modules/adapters/react/services/UIManagerModuleWrapper.java +13 -0
  7. package/android/src/main/java/expo/modules/core/ViewManager.java +9 -0
  8. package/android/src/main/java/expo/modules/core/interfaces/JavaScriptContextProvider.java +4 -0
  9. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +37 -1
  10. package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +19 -0
  11. package/android/src/main/java/expo/modules/core/interfaces/services/UIManager.java +2 -0
  12. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +13 -5
  13. package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +2 -13
  14. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +11 -5
  15. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +5 -1
  16. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +17 -0
  17. package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +14 -3
  18. package/android/src/main/java/expo/modules/kotlin/events/EventEmitter.kt +13 -0
  19. package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +102 -0
  20. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +25 -1
  21. package/android/src/main/java/expo/modules/kotlin/{methods/AnyMethod.kt → functions/AnyFunction.kt} +6 -5
  22. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +15 -0
  23. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +61 -0
  24. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +15 -0
  25. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +36 -0
  26. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +14 -0
  27. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +176 -27
  28. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +2 -2
  29. package/android/src/main/java/expo/modules/kotlin/records/FieldValidator.kt +139 -0
  30. package/android/src/main/java/expo/modules/kotlin/records/Record.kt +0 -39
  31. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +59 -10
  32. package/android/src/main/java/expo/modules/kotlin/records/Required.kt +5 -0
  33. package/android/src/main/java/expo/modules/kotlin/records/ValidationBinder.kt +110 -0
  34. package/android/src/main/java/expo/modules/kotlin/records/Validators.kt +61 -0
  35. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +35 -0
  36. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverterHelper.kt +148 -0
  37. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +9 -1
  38. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +49 -0
  39. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinition.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +64 -0
  41. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +4 -1
  42. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +15 -2
  43. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +3 -0
  44. package/build/NativeModulesProxy.native.d.ts +0 -4
  45. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  46. package/build/NativeModulesProxy.native.js +1 -14
  47. package/build/NativeModulesProxy.native.js.map +1 -1
  48. package/build/NativeModulesProxy.types.d.ts +0 -3
  49. package/build/NativeModulesProxy.types.d.ts.map +1 -1
  50. package/build/NativeModulesProxy.types.js.map +1 -1
  51. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  52. package/build/NativeViewManagerAdapter.native.js +9 -33
  53. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  54. package/build/sweet/NativeErrorManager.js +1 -1
  55. package/build/sweet/NativeErrorManager.js.map +1 -1
  56. package/ios/AppDelegates/EXAppDelegatesLoader.m +4 -8
  57. package/ios/AppDelegates/ExpoAppDelegate.swift +4 -10
  58. package/ios/EXAppDefines.h +1 -0
  59. package/ios/EXAppDefines.m +6 -0
  60. package/ios/EXUtilities.h +2 -0
  61. package/ios/EXUtilities.m +12 -0
  62. package/ios/ExpoModulesCore.h +4 -0
  63. package/ios/ExpoModulesCore.podspec +4 -2
  64. package/ios/Interfaces/FileSystem/EXFileSystemInterface.h +1 -1
  65. package/ios/Interfaces/TaskManager/EXTaskServiceInterface.h +1 -0
  66. package/ios/JSI/{JSIConversions.h → EXJSIConversions.h} +4 -1
  67. package/ios/JSI/{JSIConversions.mm → EXJSIConversions.mm} +16 -5
  68. package/ios/JSI/{JSIInstaller.h → EXJSIInstaller.h} +3 -3
  69. package/ios/JSI/EXJSIInstaller.mm +17 -0
  70. package/ios/JSI/{ExpoModulesProxySpec.h → EXJSIUtils.h} +0 -9
  71. package/ios/JSI/{ExpoModulesProxySpec.mm → EXJSIUtils.mm} +4 -48
  72. package/ios/JSI/EXJavaScriptObject.h +97 -0
  73. package/ios/JSI/EXJavaScriptObject.mm +121 -0
  74. package/ios/JSI/{JavaScriptRuntime.h → EXJavaScriptRuntime.h} +27 -8
  75. package/ios/JSI/EXJavaScriptRuntime.mm +153 -0
  76. package/ios/JSI/EXJavaScriptValue.h +57 -0
  77. package/ios/JSI/EXJavaScriptValue.mm +166 -0
  78. package/ios/JSI/ExpoModulesHostObject.mm +2 -1
  79. package/ios/JSI/JavaScriptRuntime.swift +32 -0
  80. package/ios/JSI/JavaScriptValue.swift +94 -0
  81. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -11
  82. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +2 -2
  83. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +24 -22
  84. package/ios/ReactDelegates/EXReactCompatibleHelpers.h +18 -0
  85. package/ios/ReactDelegates/EXReactCompatibleHelpers.m +19 -0
  86. package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
  87. package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +1 -1
  88. package/ios/Swift/AppContext.swift +27 -1
  89. package/ios/Swift/Functions/AsyncFunction.swift +17 -0
  90. package/ios/Swift/Functions/ConcreteFunction.swift +6 -1
  91. package/ios/Swift/JavaScriptUtils.swift +11 -0
  92. package/ios/Swift/ModuleHolder.swift +14 -3
  93. package/ios/Swift/ModulesProvider.swift +3 -10
  94. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +176 -0
  95. package/ios/Swift/SwiftInteropBridge.swift +14 -5
  96. package/ios/Swift/Views/ComponentData.swift +2 -1
  97. package/ios/Swift/Views/ExpoView.swift +8 -0
  98. package/ios/Swift.h +5 -0
  99. package/ios/Tests/ArgumentTypeSpec.swift +2 -3
  100. package/ios/Tests/ConstantsSpec.swift +2 -3
  101. package/ios/Tests/ConvertiblesSpec.swift +2 -3
  102. package/ios/Tests/ExceptionsSpec.swift +2 -3
  103. package/ios/Tests/ExpoModulesSpec.swift +76 -0
  104. package/ios/Tests/FunctionSpec.swift +2 -3
  105. package/ios/Tests/FunctionWithConvertiblesSpec.swift +2 -3
  106. package/ios/Tests/JavaScriptObjectSpec.swift +97 -0
  107. package/ios/Tests/JavaScriptRuntimeSpec.swift +94 -0
  108. package/ios/Tests/ModuleEventListenersSpec.swift +2 -3
  109. package/ios/Tests/ModuleRegistrySpec.swift +2 -3
  110. package/ios/Tests/RecordSpec.swift +2 -3
  111. package/package.json +2 -2
  112. package/src/NativeModulesProxy.native.ts +2 -22
  113. package/src/NativeModulesProxy.types.ts +0 -8
  114. package/src/NativeViewManagerAdapter.native.tsx +12 -28
  115. package/src/sweet/NativeErrorManager.ts +1 -1
  116. package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +0 -26
  117. package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +0 -14
  118. package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +0 -15
  119. package/ios/JSI/JSIInstaller.mm +0 -34
  120. package/ios/JSI/JavaScriptObject.h +0 -60
  121. package/ios/JSI/JavaScriptObject.mm +0 -93
  122. package/ios/JSI/JavaScriptRuntime.mm +0 -102
  123. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.h +0 -16
  124. package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.m +0 -28
@@ -23,22 +23,24 @@ import expo.modules.kotlin.events.EventListenerWithSenderAndPayload
23
23
  import expo.modules.kotlin.events.EventName
24
24
  import expo.modules.kotlin.events.EventsDefinition
25
25
  import expo.modules.kotlin.events.OnActivityResultPayload
26
- import expo.modules.kotlin.methods.AnyMethod
27
- import expo.modules.kotlin.methods.Method
28
- import expo.modules.kotlin.methods.PromiseMethod
26
+ import expo.modules.kotlin.functions.AnyFunction
27
+ import expo.modules.kotlin.functions.AsyncFunction
28
+ import expo.modules.kotlin.functions.AsyncFunctionWithPromise
29
+ import expo.modules.kotlin.functions.AsyncFunctionBuilder
29
30
  import expo.modules.kotlin.types.toAnyType
30
31
  import expo.modules.kotlin.views.ViewManagerDefinition
31
32
  import expo.modules.kotlin.views.ViewManagerDefinitionBuilder
32
33
  import kotlin.reflect.typeOf
33
34
 
34
35
  @DefinitionMarker
35
- class ModuleDefinitionBuilder(private val module: Module? = null) {
36
+ class ModuleDefinitionBuilder(@PublishedApi internal val module: Module? = null) {
36
37
  private var name: String? = null
37
38
  private var constantsProvider = { emptyMap<String, Any?>() }
38
39
  private var eventsDefinition: EventsDefinition? = null
40
+ private var functionBuilders = mutableListOf<AsyncFunctionBuilder>()
39
41
 
40
42
  @PublishedApi
41
- internal var methods = mutableMapOf<String, AnyMethod>()
43
+ internal var methods = mutableMapOf<String, AnyFunction>()
42
44
 
43
45
  @PublishedApi
44
46
  internal var viewManagerDefinition: ViewManagerDefinition? = null
@@ -52,7 +54,7 @@ class ModuleDefinitionBuilder(private val module: Module? = null) {
52
54
  return ModuleDefinitionData(
53
55
  requireNotNull(moduleName),
54
56
  constantsProvider,
55
- methods,
57
+ methods + functionBuilders.associate { it.build() },
56
58
  viewManagerDefinition,
57
59
  eventListeners,
58
60
  eventsDefinition
@@ -80,109 +82,256 @@ class ModuleDefinitionBuilder(private val module: Module? = null) {
80
82
  constantsProvider = { constants.toMap() }
81
83
  }
82
84
 
83
- @JvmName("methodWithoutArgs")
85
+ @Deprecated(
86
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
87
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
88
+ )
89
+ @JvmName("functionWithoutArgs")
84
90
  inline fun function(
85
91
  name: String,
86
92
  crossinline body: () -> Any?
87
93
  ) {
88
- methods[name] = Method(name, arrayOf()) { body() }
94
+ methods[name] = AsyncFunction(name, arrayOf()) { body() }
89
95
  }
90
96
 
97
+ @Deprecated(
98
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
99
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
100
+ )
91
101
  inline fun <reified R> function(
92
102
  name: String,
93
103
  crossinline body: () -> R
94
104
  ) {
95
- methods[name] = Method(name, arrayOf()) { body() }
105
+ methods[name] = AsyncFunction(name, arrayOf()) { body() }
96
106
  }
97
107
 
108
+ @Deprecated(
109
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
110
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
111
+ )
98
112
  inline fun <reified R, reified P0> function(
99
113
  name: String,
100
114
  crossinline body: (p0: P0) -> R
101
115
  ) {
102
116
  methods[name] = if (P0::class == Promise::class) {
103
- PromiseMethod(name, arrayOf()) { _, promise -> body(promise as P0) }
117
+ AsyncFunctionWithPromise(name, arrayOf()) { _, promise -> body(promise as P0) }
104
118
  } else {
105
- Method(name, arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }
119
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }
106
120
  }
107
121
  }
108
122
 
123
+ @Deprecated(
124
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
125
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
126
+ )
109
127
  inline fun <reified R, reified P0, reified P1> function(
110
128
  name: String,
111
129
  crossinline body: (p0: P0, p1: P1) -> R
112
130
  ) {
113
131
  methods[name] = if (P1::class == Promise::class) {
114
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
132
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
115
133
  } else {
116
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }
134
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }
117
135
  }
118
136
  }
119
137
 
138
+ @Deprecated(
139
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
140
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
141
+ )
120
142
  inline fun <reified R, reified P0, reified P1, reified P2> function(
121
143
  name: String,
122
144
  crossinline body: (p0: P0, p1: P1, p2: P2) -> R
123
145
  ) {
124
146
  methods[name] = if (P2::class == Promise::class) {
125
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, promise as P2) }
147
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, promise as P2) }
126
148
  } else {
127
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }
149
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }
128
150
  }
129
151
  }
130
152
 
153
+ @Deprecated(
154
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
155
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
156
+ )
131
157
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> function(
132
158
  name: String,
133
159
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R
134
160
  ) {
135
161
  methods[name] = if (P3::class == Promise::class) {
136
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, promise as P3) }
162
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, promise as P3) }
137
163
  } else {
138
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
164
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
139
165
  }
140
166
  }
141
167
 
168
+ @Deprecated(
169
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
170
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
171
+ )
142
172
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> function(
143
173
  name: String,
144
174
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R
145
175
  ) {
146
176
  methods[name] = if (P4::class == Promise::class) {
147
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, promise as P4) }
177
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, promise as P4) }
148
178
  } else {
149
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
179
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
150
180
  }
151
181
  }
152
182
 
183
+ @Deprecated(
184
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
185
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
186
+ )
153
187
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> function(
154
188
  name: String,
155
189
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R
156
190
  ) {
157
191
  methods[name] = if (P5::class == Promise::class) {
158
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, promise as P5) }
192
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, promise as P5) }
159
193
  } else {
160
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
194
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
161
195
  }
162
196
  }
163
197
 
198
+ @Deprecated(
199
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
200
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
201
+ )
164
202
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> function(
165
203
  name: String,
166
204
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R
167
205
  ) {
168
206
  methods[name] = if (P6::class == Promise::class) {
169
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, promise as P6) }
207
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, promise as P6) }
170
208
  } else {
171
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
209
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
172
210
  }
173
211
  }
174
212
 
213
+ @Deprecated(
214
+ message = "The 'function' component was deprecated and will change its behavior in the future.",
215
+ replaceWith = ReplaceWith("asyncFunction(name, body)")
216
+ )
175
217
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> function(
176
218
  name: String,
177
219
  crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R
178
220
  ) {
179
221
  methods[name] = if (P7::class == Promise::class) {
180
- PromiseMethod(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, args[6] as P6, promise as P7) }
222
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, args[6] as P6, promise as P7) }
181
223
  } else {
182
- Method(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
224
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
183
225
  }
184
226
  }
185
227
 
228
+ @JvmName("asyncFunctionWithoutArgs")
229
+ inline fun asyncFunction(
230
+ name: String,
231
+ crossinline body: () -> Any?
232
+ ) {
233
+ methods[name] = AsyncFunction(name, arrayOf()) { body() }
234
+ }
235
+
236
+ inline fun <reified R> asyncFunction(
237
+ name: String,
238
+ crossinline body: () -> R
239
+ ) {
240
+ methods[name] = AsyncFunction(name, arrayOf()) { body() }
241
+ }
242
+
243
+ inline fun <reified R, reified P0> asyncFunction(
244
+ name: String,
245
+ crossinline body: (p0: P0) -> R
246
+ ) {
247
+ methods[name] = if (P0::class == Promise::class) {
248
+ AsyncFunctionWithPromise(name, arrayOf()) { _, promise -> body(promise as P0) }
249
+ } else {
250
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }
251
+ }
252
+ }
253
+
254
+ inline fun <reified R, reified P0, reified P1> asyncFunction(
255
+ name: String,
256
+ crossinline body: (p0: P0, p1: P1) -> R
257
+ ) {
258
+ methods[name] = if (P1::class == Promise::class) {
259
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
260
+ } else {
261
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }
262
+ }
263
+ }
264
+
265
+ inline fun <reified R, reified P0, reified P1, reified P2> asyncFunction(
266
+ name: String,
267
+ crossinline body: (p0: P0, p1: P1, p2: P2) -> R
268
+ ) {
269
+ methods[name] = if (P2::class == Promise::class) {
270
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, promise as P2) }
271
+ } else {
272
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }
273
+ }
274
+ }
275
+
276
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3> asyncFunction(
277
+ name: String,
278
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> R
279
+ ) {
280
+ methods[name] = if (P3::class == Promise::class) {
281
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, promise as P3) }
282
+ } else {
283
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
284
+ }
285
+ }
286
+
287
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> asyncFunction(
288
+ name: String,
289
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R
290
+ ) {
291
+ methods[name] = if (P4::class == Promise::class) {
292
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, promise as P4) }
293
+ } else {
294
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
295
+ }
296
+ }
297
+
298
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> asyncFunction(
299
+ name: String,
300
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R
301
+ ) {
302
+ methods[name] = if (P5::class == Promise::class) {
303
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, promise as P5) }
304
+ } else {
305
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
306
+ }
307
+ }
308
+
309
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> asyncFunction(
310
+ name: String,
311
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R
312
+ ) {
313
+ methods[name] = if (P6::class == Promise::class) {
314
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, promise as P6) }
315
+ } else {
316
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
317
+ }
318
+ }
319
+
320
+ inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> asyncFunction(
321
+ name: String,
322
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R
323
+ ) {
324
+ methods[name] = if (P7::class == Promise::class) {
325
+ AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { args, promise -> body(args[0] as P0, args[1] as P1, args[2] as P2, args[3] as P3, args[4] as P4, args[5] as P5, args[6] as P6, promise as P7) }
326
+ } else {
327
+ AsyncFunction(name, arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
328
+ }
329
+ }
330
+
331
+ fun asyncFunction(
332
+ name: String
333
+ ) = AsyncFunctionBuilder(name).also { functionBuilders.add(it) }
334
+
186
335
  /**
187
336
  * Creates the view manager definition that scopes other view-related definitions.
188
337
  */
@@ -240,14 +389,14 @@ class ModuleDefinitionBuilder(private val module: Module? = null) {
240
389
  * Creates module's lifecycle listener that is called right after the first event listener is added.
241
390
  */
242
391
  inline fun onStartObserving(crossinline body: () -> Unit) {
243
- function("startObserving", body)
392
+ asyncFunction("startObserving", body)
244
393
  }
245
394
 
246
395
  /**
247
396
  * Creates module's lifecycle listener that is called right after all event listeners are removed.
248
397
  */
249
398
  inline fun onStopObserving(crossinline body: () -> Unit) {
250
- function("stopObserving", body)
399
+ asyncFunction("stopObserving", body)
251
400
  }
252
401
 
253
402
  /**
@@ -3,13 +3,13 @@ package expo.modules.kotlin.modules
3
3
  import expo.modules.kotlin.events.EventListener
4
4
  import expo.modules.kotlin.events.EventName
5
5
  import expo.modules.kotlin.events.EventsDefinition
6
- import expo.modules.kotlin.methods.AnyMethod
6
+ import expo.modules.kotlin.functions.AnyFunction
7
7
  import expo.modules.kotlin.views.ViewManagerDefinition
8
8
 
9
9
  class ModuleDefinitionData(
10
10
  val name: String,
11
11
  val constantsProvider: () -> Map<String, Any?>,
12
- val methods: Map<String, AnyMethod>,
12
+ val methods: Map<String, AnyFunction>,
13
13
  val viewManagerDefinition: ViewManagerDefinition? = null,
14
14
  val eventListeners: Map<EventName, EventListener> = emptyMap(),
15
15
  val eventsDefinition: EventsDefinition? = null
@@ -0,0 +1,139 @@
1
+ package expo.modules.kotlin.records
2
+
3
+ import expo.modules.kotlin.exception.ValidationException
4
+
5
+ interface FieldValidator<T> {
6
+ fun validate(value: T)
7
+ }
8
+
9
+ class NumericRangeValidator<T : Comparable<T>>(
10
+ private val from: T,
11
+ private val to: T,
12
+ private val fromInclusive: Boolean,
13
+ private val toInclusive: Boolean
14
+ ) : FieldValidator<T> {
15
+ override fun validate(value: T) {
16
+ if (
17
+ value < from ||
18
+ to < value ||
19
+ value == from && !fromInclusive ||
20
+ value == to && !toInclusive
21
+ ) {
22
+ throw ValidationException("Value should be in range $from ${if (fromInclusive) "<=" else "<"} 'value' ${if (toInclusive) "<=" else "<"} $to, got $value")
23
+ }
24
+ }
25
+ }
26
+
27
+ class IsNotEmptyCollectionValidator : FieldValidator<Collection<*>> {
28
+ override fun validate(value: Collection<*>) {
29
+ if (value.isEmpty()) {
30
+ throw ValidationException("Collection is empty")
31
+ }
32
+ }
33
+ }
34
+
35
+ class IsNotEmptyIntArrayValidator : FieldValidator<IntArray> {
36
+ override fun validate(value: IntArray) {
37
+ if (value.isEmpty()) {
38
+ throw ValidationException("Array is empty")
39
+ }
40
+ }
41
+ }
42
+
43
+ class IsNotEmptyFloatArrayValidator : FieldValidator<FloatArray> {
44
+ override fun validate(value: FloatArray) {
45
+ if (value.isEmpty()) {
46
+ throw ValidationException("Array is empty")
47
+ }
48
+ }
49
+ }
50
+
51
+ class IsNotEmptyDoubleArrayValidator : FieldValidator<DoubleArray> {
52
+ override fun validate(value: DoubleArray) {
53
+ if (value.isEmpty()) {
54
+ throw ValidationException("Array is empty")
55
+ }
56
+ }
57
+ }
58
+
59
+ class IsNotEmptyArrayValidator : FieldValidator<Array<*>> {
60
+ override fun validate(value: Array<*>) {
61
+ if (value.isEmpty()) {
62
+ throw ValidationException("Array is empty")
63
+ }
64
+ }
65
+ }
66
+
67
+ class CollectionSizeValidator(
68
+ private val min: Int,
69
+ private val max: Int
70
+ ) : FieldValidator<Collection<*>> {
71
+ override fun validate(value: Collection<*>) {
72
+ if (value.size < min || value.size > max) {
73
+ throw ValidationException("Number of elements in the collection should be between $min and $max, got ${value.size}")
74
+ }
75
+ }
76
+ }
77
+
78
+ class IntArraySizeValidator(
79
+ private val min: Int,
80
+ private val max: Int
81
+ ) : FieldValidator<IntArray> {
82
+ override fun validate(value: IntArray) {
83
+ if (value.size < min || value.size > max) {
84
+ throw ValidationException("Number of elements in the array should be between $min and $max, got ${value.size}")
85
+ }
86
+ }
87
+ }
88
+
89
+ class DoubleArraySizeValidator(
90
+ private val min: Int,
91
+ private val max: Int
92
+ ) : FieldValidator<DoubleArray> {
93
+ override fun validate(value: DoubleArray) {
94
+ if (value.size < min || value.size > max) {
95
+ throw ValidationException("Number of elements in the array should be between $min and $max, got ${value.size}")
96
+ }
97
+ }
98
+ }
99
+
100
+ class FloatArraySizeValidator(
101
+ private val min: Int,
102
+ private val max: Int
103
+ ) : FieldValidator<FloatArray> {
104
+ override fun validate(value: FloatArray) {
105
+ if (value.size < min || value.size > max) {
106
+ throw ValidationException("Number of elements in the array should be between $min and $max, got ${value.size}")
107
+ }
108
+ }
109
+ }
110
+
111
+ class ArraySizeValidator(
112
+ private val min: Int,
113
+ private val max: Int
114
+ ) : FieldValidator<Array<*>> {
115
+ override fun validate(value: Array<*>) {
116
+ if (value.size < min || value.size > max) {
117
+ throw ValidationException("Number of elements in the array should be between $min and $max, got ${value.size}")
118
+ }
119
+ }
120
+ }
121
+
122
+ class StringSizeValidator(
123
+ private val min: Int,
124
+ private val max: Int
125
+ ) : FieldValidator<String> {
126
+ override fun validate(value: String) {
127
+ if (value.length < min || value.length > max) {
128
+ throw ValidationException("Length of the string should be between $min and $max, got $value (${value.length} characters)")
129
+ }
130
+ }
131
+ }
132
+
133
+ class RegexValidator(private val regex: Regex) : FieldValidator<CharSequence> {
134
+ override fun validate(value: CharSequence) {
135
+ if (!regex.matches(value)) {
136
+ throw ValidationException("Provided string $value didn't match regex $regex")
137
+ }
138
+ }
139
+ }
@@ -1,42 +1,3 @@
1
1
  package expo.modules.kotlin.records
2
2
 
3
- import android.os.Bundle
4
- import com.facebook.react.bridge.Arguments
5
- import com.facebook.react.bridge.ReadableArray
6
- import com.facebook.react.bridge.ReadableMap
7
- import java.lang.IllegalArgumentException
8
- import kotlin.reflect.full.findAnnotation
9
- import kotlin.reflect.full.memberProperties
10
- import kotlin.reflect.jvm.isAccessible
11
-
12
3
  interface Record
13
-
14
- fun Record.toJSMap(): ReadableMap {
15
- val writableMap = Arguments.createMap()
16
- javaClass
17
- .kotlin
18
- .memberProperties.map { property ->
19
- val fieldInformation = property.findAnnotation<Field>() ?: return@map
20
- val jsKey = fieldInformation.key.takeUnless { it == "" } ?: property.name
21
-
22
- property.isAccessible = true
23
-
24
- // TODO(@lukmccall): add more sophisticated conversion method
25
- when (val value = property.get(this)) {
26
- null, is Unit -> writableMap.putNull(jsKey)
27
- is Boolean -> writableMap.putBoolean(jsKey, value)
28
- is Int -> writableMap.putInt(jsKey, value)
29
- is Number -> writableMap.putDouble(jsKey, value.toDouble())
30
- is String -> writableMap.putString(jsKey, value)
31
- is ReadableArray -> writableMap.putArray(jsKey, value)
32
- is ReadableMap -> writableMap.putMap(jsKey, value)
33
- is Record -> writableMap.putMap(jsKey, value.toJSMap())
34
- is Bundle -> writableMap.putMap(jsKey, Arguments.fromBundle(value))
35
- is List<*> -> writableMap.putArray(jsKey, Arguments.fromList(value))
36
- is Array<*> -> writableMap.putArray(jsKey, Arguments.fromArray(value))
37
- else -> throw IllegalArgumentException("Could not convert " + value.javaClass)
38
- }
39
- }
40
-
41
- return writableMap
42
- }
@@ -4,23 +4,41 @@ import com.facebook.react.bridge.Dynamic
4
4
  import expo.modules.kotlin.allocators.ObjectConstructor
5
5
  import expo.modules.kotlin.allocators.ObjectConstructorFactory
6
6
  import expo.modules.kotlin.exception.FieldCastException
7
+ import expo.modules.kotlin.exception.FieldRequiredException
7
8
  import expo.modules.kotlin.exception.RecordCastException
8
9
  import expo.modules.kotlin.exception.exceptionDecorator
9
10
  import expo.modules.kotlin.recycle
10
11
  import expo.modules.kotlin.types.TypeConverter
11
12
  import expo.modules.kotlin.types.TypeConverterProvider
12
13
  import kotlin.reflect.KClass
14
+ import kotlin.reflect.KProperty1
13
15
  import kotlin.reflect.KType
16
+ import kotlin.reflect.full.createInstance
14
17
  import kotlin.reflect.full.findAnnotation
15
18
  import kotlin.reflect.full.memberProperties
16
19
  import kotlin.reflect.jvm.javaField
17
20
 
18
- // TODO(@lukmccall): create all converters during initialization
19
21
  class RecordTypeConverter<T : Record>(
20
22
  private val converterProvider: TypeConverterProvider,
21
23
  val type: KType,
22
24
  ) : TypeConverter<T>(type.isMarkedNullable) {
23
25
  private val objectConstructorFactory = ObjectConstructorFactory()
26
+ private val propertyDescriptors: Map<KProperty1<out Any, *>, PropertyDescriptor> =
27
+ (type.classifier as KClass<*>)
28
+ .memberProperties
29
+ .map { property ->
30
+ val fieldAnnotation = property.findAnnotation<Field>() ?: return@map null
31
+ val typeConverter = converterProvider.obtainTypeConverter(property.returnType)
32
+
33
+ return@map property to PropertyDescriptor(
34
+ typeConverter,
35
+ fieldAnnotation,
36
+ isRequired = property.findAnnotation<Required>() != null,
37
+ validators = getValidators(property)
38
+ )
39
+ }
40
+ .filterNotNull()
41
+ .toMap()
24
42
 
25
43
  override fun convertNonOptional(value: Dynamic): T = exceptionDecorator({ cause -> RecordCastException(type, cause) }) {
26
44
  val jsMap = value.asMap()
@@ -28,23 +46,32 @@ class RecordTypeConverter<T : Record>(
28
46
  val kClass = type.classifier as KClass<*>
29
47
  val instance = getObjectConstructor(kClass.java).construct()
30
48
 
31
- kClass
32
- .memberProperties
33
- .map { property ->
34
- val filedInformation = property.findAnnotation<Field>() ?: return@map
35
- val jsKey = filedInformation.key.takeUnless { it == "" } ?: property.name
49
+ propertyDescriptors
50
+ .forEach { (property, descriptor) ->
51
+ val jsKey = descriptor.fieldAnnotation.key.takeUnless { it.isBlank() } ?: property.name
36
52
 
37
53
  if (!jsMap.hasKey(jsKey)) {
38
- // TODO(@lukmccall): handle required keys
39
- return@map
54
+ if (descriptor.isRequired) {
55
+ throw FieldRequiredException(property)
56
+ }
57
+
58
+ return@forEach
40
59
  }
41
60
 
42
61
  jsMap.getDynamic(jsKey).recycle {
43
62
  val javaField = property.javaField!!
44
63
 
45
- val elementConverter = converterProvider.obtainTypeConverter(property.returnType)
46
64
  val casted = exceptionDecorator({ cause -> FieldCastException(property.name, property.returnType, type, cause) }) {
47
- elementConverter.convert(this)
65
+ descriptor.typeConverter.convert(this)
66
+ }
67
+
68
+ if (casted != null) {
69
+ descriptor
70
+ .validators
71
+ .forEach { validator ->
72
+ @Suppress("UNCHECKED_CAST")
73
+ (validator as FieldValidator<Any>).validate(casted)
74
+ }
48
75
  }
49
76
 
50
77
  javaField.isAccessible = true
@@ -59,4 +86,26 @@ class RecordTypeConverter<T : Record>(
59
86
  private fun <T> getObjectConstructor(clazz: Class<T>): ObjectConstructor<T> {
60
87
  return objectConstructorFactory.get(clazz)
61
88
  }
89
+
90
+ private fun getValidators(property: KProperty1<out Any, *>): List<FieldValidator<*>> {
91
+ return property
92
+ .annotations
93
+ .map findValidators@{ annotation ->
94
+ val binderAnnotation = annotation.annotationClass.findAnnotation<BindUsing>()
95
+ ?: return@findValidators null
96
+ annotation to binderAnnotation
97
+ }
98
+ .filterNotNull()
99
+ .map { (annotation, binderAnnotation) ->
100
+ val binderInstance = binderAnnotation.binder.createInstance() as ValidationBinder
101
+ binderInstance.bind(annotation, property.returnType)
102
+ }
103
+ }
104
+
105
+ private data class PropertyDescriptor(
106
+ val typeConverter: TypeConverter<*>,
107
+ val fieldAnnotation: Field,
108
+ val isRequired: Boolean,
109
+ val validators: List<FieldValidator<*>>
110
+ )
62
111
  }
@@ -0,0 +1,5 @@
1
+ package expo.modules.kotlin.records
2
+
3
+ @Target(AnnotationTarget.PROPERTY)
4
+ @Retention(AnnotationRetention.RUNTIME)
5
+ annotation class Required