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.
- package/CHANGELOG.md +33 -0
- package/README.md +1 -1
- package/android/ExpoModulesCorePlugin.gradle +15 -0
- package/android/build.gradle +46 -32
- package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +5 -5
- package/android/src/main/java/expo/modules/adapters/react/services/UIManagerModuleWrapper.java +13 -0
- package/android/src/main/java/expo/modules/core/ViewManager.java +9 -0
- package/android/src/main/java/expo/modules/core/interfaces/JavaScriptContextProvider.java +4 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +37 -1
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +19 -0
- package/android/src/main/java/expo/modules/core/interfaces/services/UIManager.java +2 -0
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +13 -5
- package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +2 -13
- package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +11 -5
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +5 -1
- package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +17 -0
- package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +14 -3
- package/android/src/main/java/expo/modules/kotlin/events/EventEmitter.kt +13 -0
- package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +102 -0
- package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +25 -1
- package/android/src/main/java/expo/modules/kotlin/{methods/AnyMethod.kt → functions/AnyFunction.kt} +6 -5
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +61 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +36 -0
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +176 -27
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +2 -2
- package/android/src/main/java/expo/modules/kotlin/records/FieldValidator.kt +139 -0
- package/android/src/main/java/expo/modules/kotlin/records/Record.kt +0 -39
- package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +59 -10
- package/android/src/main/java/expo/modules/kotlin/records/Required.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/records/ValidationBinder.kt +110 -0
- package/android/src/main/java/expo/modules/kotlin/records/Validators.kt +61 -0
- package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +35 -0
- package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverterHelper.kt +148 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +9 -1
- package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +49 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinition.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +64 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +4 -1
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +15 -2
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +3 -0
- package/build/NativeModulesProxy.native.d.ts +0 -4
- package/build/NativeModulesProxy.native.d.ts.map +1 -1
- package/build/NativeModulesProxy.native.js +1 -14
- package/build/NativeModulesProxy.native.js.map +1 -1
- package/build/NativeModulesProxy.types.d.ts +0 -3
- package/build/NativeModulesProxy.types.d.ts.map +1 -1
- package/build/NativeModulesProxy.types.js.map +1 -1
- package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
- package/build/NativeViewManagerAdapter.native.js +9 -33
- package/build/NativeViewManagerAdapter.native.js.map +1 -1
- package/build/sweet/NativeErrorManager.js +1 -1
- package/build/sweet/NativeErrorManager.js.map +1 -1
- package/ios/AppDelegates/EXAppDelegatesLoader.m +4 -8
- package/ios/AppDelegates/ExpoAppDelegate.swift +4 -10
- package/ios/EXAppDefines.h +1 -0
- package/ios/EXAppDefines.m +6 -0
- package/ios/EXUtilities.h +2 -0
- package/ios/EXUtilities.m +12 -0
- package/ios/ExpoModulesCore.h +4 -0
- package/ios/ExpoModulesCore.podspec +4 -2
- package/ios/Interfaces/FileSystem/EXFileSystemInterface.h +1 -1
- package/ios/Interfaces/TaskManager/EXTaskServiceInterface.h +1 -0
- package/ios/JSI/{JSIConversions.h → EXJSIConversions.h} +4 -1
- package/ios/JSI/{JSIConversions.mm → EXJSIConversions.mm} +16 -5
- package/ios/JSI/{JSIInstaller.h → EXJSIInstaller.h} +3 -3
- package/ios/JSI/EXJSIInstaller.mm +17 -0
- package/ios/JSI/{ExpoModulesProxySpec.h → EXJSIUtils.h} +0 -9
- package/ios/JSI/{ExpoModulesProxySpec.mm → EXJSIUtils.mm} +4 -48
- package/ios/JSI/EXJavaScriptObject.h +97 -0
- package/ios/JSI/EXJavaScriptObject.mm +121 -0
- package/ios/JSI/{JavaScriptRuntime.h → EXJavaScriptRuntime.h} +27 -8
- package/ios/JSI/EXJavaScriptRuntime.mm +153 -0
- package/ios/JSI/EXJavaScriptValue.h +57 -0
- package/ios/JSI/EXJavaScriptValue.mm +166 -0
- package/ios/JSI/ExpoModulesHostObject.mm +2 -1
- package/ios/JSI/JavaScriptRuntime.swift +32 -0
- package/ios/JSI/JavaScriptValue.swift +94 -0
- package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -11
- package/ios/NativeModulesProxy/EXNativeModulesProxy.h +2 -2
- package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +24 -22
- package/ios/ReactDelegates/EXReactCompatibleHelpers.h +18 -0
- package/ios/ReactDelegates/EXReactCompatibleHelpers.m +19 -0
- package/ios/ReactDelegates/ExpoReactDelegate.swift +2 -2
- package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +1 -1
- package/ios/Swift/AppContext.swift +27 -1
- package/ios/Swift/Functions/AsyncFunction.swift +17 -0
- package/ios/Swift/Functions/ConcreteFunction.swift +6 -1
- package/ios/Swift/JavaScriptUtils.swift +11 -0
- package/ios/Swift/ModuleHolder.swift +14 -3
- package/ios/Swift/ModulesProvider.swift +3 -10
- package/ios/Swift/Objects/ObjectDefinitionComponents.swift +176 -0
- package/ios/Swift/SwiftInteropBridge.swift +14 -5
- package/ios/Swift/Views/ComponentData.swift +2 -1
- package/ios/Swift/Views/ExpoView.swift +8 -0
- package/ios/Swift.h +5 -0
- package/ios/Tests/ArgumentTypeSpec.swift +2 -3
- package/ios/Tests/ConstantsSpec.swift +2 -3
- package/ios/Tests/ConvertiblesSpec.swift +2 -3
- package/ios/Tests/ExceptionsSpec.swift +2 -3
- package/ios/Tests/ExpoModulesSpec.swift +76 -0
- package/ios/Tests/FunctionSpec.swift +2 -3
- package/ios/Tests/FunctionWithConvertiblesSpec.swift +2 -3
- package/ios/Tests/JavaScriptObjectSpec.swift +97 -0
- package/ios/Tests/JavaScriptRuntimeSpec.swift +94 -0
- package/ios/Tests/ModuleEventListenersSpec.swift +2 -3
- package/ios/Tests/ModuleRegistrySpec.swift +2 -3
- package/ios/Tests/RecordSpec.swift +2 -3
- package/package.json +2 -2
- package/src/NativeModulesProxy.native.ts +2 -22
- package/src/NativeModulesProxy.types.ts +0 -8
- package/src/NativeViewManagerAdapter.native.tsx +12 -28
- package/src/sweet/NativeErrorManager.ts +1 -1
- package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +0 -26
- package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +0 -14
- package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +0 -15
- package/ios/JSI/JSIInstaller.mm +0 -34
- package/ios/JSI/JavaScriptObject.h +0 -60
- package/ios/JSI/JavaScriptObject.mm +0 -93
- package/ios/JSI/JavaScriptRuntime.mm +0 -102
- package/ios/NativeModulesProxy/EXComponentDataCompatibleWrapper.h +0 -16
- 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.
|
|
27
|
-
import expo.modules.kotlin.
|
|
28
|
-
import expo.modules.kotlin.
|
|
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(
|
|
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,
|
|
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
|
-
@
|
|
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] =
|
|
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] =
|
|
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
|
-
|
|
117
|
+
AsyncFunctionWithPromise(name, arrayOf()) { _, promise -> body(promise as P0) }
|
|
104
118
|
} else {
|
|
105
|
-
|
|
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
|
-
|
|
132
|
+
AsyncFunctionWithPromise(name, arrayOf(typeOf<P0>().toAnyType())) { args, promise -> body(args[0] as P0, promise as P1) }
|
|
115
133
|
} else {
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
}
|