diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8415317..5af7d30 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,13 +16,14 @@ plugins { alias(libs.plugins.ceres.android.application) alias(libs.plugins.ceres.android.application.compose) + alias(libs.plugins.ksp) } android { namespace = "dev.teogor.drifter.demo" defaultConfig { - applicationId = "dev.teogor.drifter.demo" + applicationId = "com.zeoowl.lwp.aquarium" versionCode = 1 versionName = "1.0.0-alpha01" @@ -51,12 +52,16 @@ android { } dependencies { - implementation(project(":module-unity")) + implementation(projects.moduleUnity) - implementation(project(":drifter-compose")) - implementation(project(":drifter-core")) - implementation(project(":drifter-integration")) - implementation(project(":drifter-wallpaper")) + implementation(projects.runtime) + ksp(projects.ksp) + + implementation(projects.drifterCommon) + implementation(projects.drifterCompose) + implementation(projects.drifterCore) + implementation(projects.drifterIntegration) + implementation(projects.drifterWallpaper) implementation(libs.gson) implementation(libs.core.ktx) @@ -75,3 +80,7 @@ dependencies { debugImplementation(libs.ui.tooling) debugImplementation(libs.ui.test.manifest) } + +ksp { + logging.captureStandardError(LogLevel.INFO) +} diff --git a/app/src/main/kotlin/dev/teogor/drifter/common/StatusBarHeightPlugin.kt b/app/src/main/kotlin/dev/teogor/drifter/common/StatusBarHeightPlugin.kt new file mode 100644 index 0000000..af290cb --- /dev/null +++ b/app/src/main/kotlin/dev/teogor/drifter/common/StatusBarHeightPlugin.kt @@ -0,0 +1,6 @@ +package dev.teogor.drifter.common + +object StatusBarHeightPlugin { + val statusBarHeight: Int + get() { return 200 } +} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/AquariumModule.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/AquariumModule.kt new file mode 100644 index 0000000..f888a19 --- /dev/null +++ b/app/src/main/kotlin/dev/teogor/drifter/demo/AquariumModule.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.demo + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import dev.teogor.drifter.DrifterEncoder +import dev.teogor.drifter.DrifterMappingKey +import dev.teogor.drifter.DrifterModule +import dev.teogor.drifter.DrifterUnityMethod +import dev.teogor.drifter.demo.models.CycleOption + +@DrifterModule( + name = "Aquarium", + receiver = "BridgeController", + methods = AquariumMethods::class, +) +data class AquariumModule( + @DrifterMappingKey( + exposedMethod = "SetEditorMode", + ) + val isEditorMode: Boolean? = null, + val waterColor: Color? = null, + val animated: Boolean? = null, + val cycleOption: CycleOption, + val statusBarColor: Color, + val statusBarIsVisible: Boolean, + val statusBarOpacity: Float, + val statusBarHeight: Int, +) + +interface AquariumMethods { + @DrifterUnityMethod( + name = "animateToWaterColor", + parameters = [ + "waterColor", + "animated", + ], + ) + fun animateToWaterColor() +} + +@DrifterEncoder +fun Color.encodeFromColor() = toArgb() diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityStorage.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/AquariumStorage.kt similarity index 57% rename from app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityStorage.kt rename to app/src/main/kotlin/dev/teogor/drifter/demo/AquariumStorage.kt index dfe6d9c..603e23a 100644 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityStorage.kt +++ b/app/src/main/kotlin/dev/teogor/drifter/demo/AquariumStorage.kt @@ -14,44 +14,44 @@ * limitations under the License. */ -package dev.teogor.drifter.demo.unity +package dev.teogor.drifter.demo import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import dev.teogor.drifter.demo.unity.models.CycleOption -import dev.teogor.drifter.demo.unity.models.StorageKeys -import dev.teogor.drifter.integration.common.TypeConverter -import dev.teogor.drifter.integration.core.UnityStorageBase +import dev.teogor.drifter.common.UnityPlayerPrefs +import dev.teogor.drifter.common.UnityPrefAccess +import dev.teogor.drifter.common.createSerializable +import dev.teogor.drifter.demo.models.CycleOption -class UnityStorage : UnityStorageBase() { - - private val colorConverter = TypeConverter( - toString = { color -> color.toArgb().toString() }, - fromString = { string -> Color(string.toInt()) }, +class AquariumStorage : UnityPlayerPrefs() { + private val colorConverter = createSerializable( + encodeToString = { color -> color.toArgb().toString() }, + decodeFromString = { string -> Color(string.toInt()) }, ) + @OptIn(UnityPrefAccess::class) var waterColor: Color = Color(0xFF6FA3EF) get() { - field = getStorageElement( - key = StorageKeys.WaterColor, + field = get( + key = AquariumKeyConstants.WATER_COLOR, defaultValue = field, converter = colorConverter, ) return field } set(value) { - println("Write Color ::") - field = writeElement( - key = StorageKeys.WaterColor, - content = value, + field = set( + key = AquariumKeyConstants.WATER_COLOR, + value = value, converter = colorConverter, ) } + @OptIn(UnityPrefAccess::class) var cycleOption: CycleOption = CycleOption.Day get() { - val cycleOptionContent = getStorageElement( - key = StorageKeys.CycleOption, + val cycleOptionContent = get( + key = AquariumKeyConstants.CYCLE_OPTION, defaultValue = field.ordinal, ) field = CycleOption.entries[cycleOptionContent] @@ -59,9 +59,9 @@ class UnityStorage : UnityStorageBase() { } set(value) { field = value - writeElement( - key = StorageKeys.CycleOption, - content = value.ordinal, + set( + key = AquariumKeyConstants.CYCLE_OPTION, + value = value.ordinal, ) } } diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/MainActivity.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/MainActivity.kt index f7cadfe..db43ca5 100644 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/MainActivity.kt +++ b/app/src/main/kotlin/dev/teogor/drifter/demo/MainActivity.kt @@ -56,8 +56,6 @@ import androidx.lifecycle.Lifecycle import dev.teogor.drifter.compose.OnLifecycleEvent import dev.teogor.drifter.compose.UvpComposable import dev.teogor.drifter.demo.ui.theme.UnityViewTheme -import dev.teogor.drifter.demo.unity.UnityController -import dev.teogor.drifter.demo.unity.UnityStorage import dev.teogor.drifter.wallpaper.LiveWallpaperUtility class MainActivity : ComponentActivity() { @@ -84,8 +82,8 @@ class MainActivity : ComponentActivity() { ) { val context = LocalContext.current - val controller = UnityController() - val storage = UnityStorage() + val controller = AquariumMessageSender() + val storage = AquariumStorage() UvpComposable( modifier = Modifier @@ -98,8 +96,8 @@ class MainActivity : ComponentActivity() { onCreated = { controller.apply { setEditorMode(true) - setEditorColor(storage.waterColor, false) - setEditorCycleOption(storage.cycleOption) + animateToWaterColor(storage.waterColor, false) + cycleOption(storage.cycleOption) } }, ) @@ -128,8 +126,8 @@ class MainActivity : ComponentActivity() { @Composable fun UnityColorPicker( - controller: UnityController, - storage: UnityStorage, + controller: AquariumMessageSender, + storage: AquariumStorage, ) { val colors = listOf( Color(0xFFB19CD9), // Lavender @@ -166,14 +164,14 @@ fun UnityColorPicker( } LaunchedEffect(selectedColor) { - controller.setEditorColor(selectedColor) + controller.animateToWaterColor(selectedColor, true) } OnLifecycleEvent { _, event -> when (event) { Lifecycle.Event.ON_RESUME -> { controller.setEditorMode(true) - controller.setEditorColor(selectedColor) + controller.animateToWaterColor(selectedColor, true) } Lifecycle.Event.ON_PAUSE -> { diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/models/CycleOption.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/models/CycleOption.kt new file mode 100644 index 0000000..5f8332b --- /dev/null +++ b/app/src/main/kotlin/dev/teogor/drifter/demo/models/CycleOption.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.demo.models + +enum class CycleOption { + Day, + Night, + Auto, +} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityController.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityController.kt deleted file mode 100644 index c1ba485..0000000 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/UnityController.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023 teogor (Teodor Grigor) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.teogor.drifter.demo.unity - -import androidx.compose.ui.graphics.Color -import dev.teogor.drifter.demo.unity.models.CycleOption -import dev.teogor.drifter.demo.unity.models.UnityActionParams -import dev.teogor.drifter.demo.unity.models.UnityActions -import dev.teogor.drifter.demo.unity.models.toJsonObject -import dev.teogor.drifter.integration.core.UnityControllerBase - -class UnityController : UnityControllerBase( - receiver = "BridgeController", -) { - - fun setEditorMode(isEditorMode: Boolean) { - val params = UnityActionParams(isEditorMode = isEditorMode) - invokeAction( - UnityActions.SetEditorMode, - params.toJsonObject(), - ) - } - - fun setEditorColor(waterColor: Color, animated: Boolean = true) { - val params = UnityActionParams(waterColor = waterColor, animated = animated) - invokeAction( - UnityActions.SetWaterColor, - params.toJsonObject(), - ) - } - - fun setEditorCycleOption(cycleOption: CycleOption) { - val params = UnityActionParams(cycleOption = cycleOption) - invokeAction( - UnityActions.SetCycleOption, - params.toJsonObject(), - ) - } - -} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/CycleOption.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/CycleOption.kt deleted file mode 100644 index 35c3c71..0000000 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/CycleOption.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.teogor.drifter.demo.unity.models - -enum class CycleOption { - Day, - Night, - Auto; -} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/StorageKeys.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/StorageKeys.kt deleted file mode 100644 index f54359f..0000000 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/StorageKeys.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.teogor.drifter.demo.unity.models - -object StorageKeys { - const val WaterColor = "storage_water_color" - const val CycleOption = "storage_cycle_option" -} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActionParams.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActionParams.kt deleted file mode 100644 index 5c53194..0000000 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActionParams.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.teogor.drifter.demo.unity.models - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import org.json.JSONObject - -data class UnityActionParams( - val isEditorMode: Boolean? = null, - val waterColor: Color? = null, - val animated: Boolean? = null, - val cycleOption: CycleOption? = null -) - -fun UnityActionParams.toJsonObject(): JSONObject { - val json = JSONObject() - isEditorMode?.let { json.put("isEditorMode", it) } - waterColor?.let { json.put("waterColor", it.toArgb()) } - animated?.let { json.put("animated", it) } - cycleOption?.let { json.put("cycleOption", it) } - return json -} diff --git a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActions.kt b/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActions.kt deleted file mode 100644 index e2883fd..0000000 --- a/app/src/main/kotlin/dev/teogor/drifter/demo/unity/models/UnityActions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.teogor.drifter.demo.unity.models - -object UnityActions { - const val SetEditorMode = "SetEditorMode" - const val SetWaterColor = "SetWaterColor" - const val SetCycleOption = "SetCycleOption" -} diff --git a/build.gradle.kts b/build.gradle.kts index db100be..d37b1e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.ceres.android.application) apply false alias(libs.plugins.ceres.android.application.compose) apply false @@ -80,6 +81,8 @@ winds { val excludedModulesForWinds = listOf( ":drifter-plugin", + ":app", + ":module-unity", ) afterWindsPluginConfiguration { winds -> if (!excludedModulesForWinds.contains(path)) { @@ -118,6 +121,8 @@ val ktlintVersion = "0.50.0" val excludeModules = listOf( project.name, + "app", + "module-unity", ) subprojects { diff --git a/codegen/.gitignore b/codegen/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/codegen/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/codegen/api/codegen.api b/codegen/api/codegen.api new file mode 100644 index 0000000..f0ecdd0 --- /dev/null +++ b/codegen/api/codegen.api @@ -0,0 +1,179 @@ +public final class dev/teogor/drifter/codegen/CodeGenerator : dev/teogor/drifter/codegen/servicelocator/ServiceLocatorAccessor { + public fun (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun generate (Ljava/util/List;Ljava/util/List;)V + public fun getCodeGenConfig ()Ldev/teogor/drifter/codegen/model/CodeGenConfig; + public fun getCodeOutputStreamMaker ()Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker; +} + +public final class dev/teogor/drifter/codegen/commons/ConstantsKt { + public static final fun getUnityMessageSender ()Lcom/squareup/kotlinpoet/ClassName; +} + +public final class dev/teogor/drifter/codegen/commons/UtilsKt { + public static final fun fileBuilder (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/squareup/kotlinpoet/FileSpec; + public static final fun findCommonBase (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + public static final fun getSafe (Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/TypeName; + public static final fun getShortName (Lcom/squareup/kotlinpoet/TypeName;)Ljava/lang/String; + public static final fun sanitizePackageName (Ljava/lang/String;)Ljava/lang/String; + public static final fun toSnakeCase (Ljava/lang/String;)Ljava/lang/String; + public static final fun toTitleCase (Ljava/lang/String;Z)Ljava/lang/String; + public static synthetic fun toTitleCase$default (Ljava/lang/String;ZILjava/lang/Object;)Ljava/lang/String; + public static final fun writeWith (Lcom/squareup/kotlinpoet/FileSpec;Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun writeWith$default (Lcom/squareup/kotlinpoet/FileSpec;Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V +} + +public abstract interface class dev/teogor/drifter/codegen/facades/CodeOutputStreamMaker { + public abstract fun makeFile (Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)Ljava/io/OutputStream; +} + +public final class dev/teogor/drifter/codegen/facades/CodeOutputStreamMakerKt { + public static final fun writeTo (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Lcom/squareup/kotlinpoet/FileSpec;Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun writeTo$default (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Lcom/squareup/kotlinpoet/FileSpec;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V +} + +public abstract interface class dev/teogor/drifter/codegen/facades/Logger { + public static final field Companion Ldev/teogor/drifter/codegen/facades/Logger$Companion; + public abstract fun error (Ljava/lang/String;)V + public abstract fun exception (Ljava/lang/Throwable;)V + public abstract fun info (Ljava/lang/String;)V + public abstract fun logging (Ljava/lang/String;)V + public abstract fun warn (Ljava/lang/String;)V +} + +public final class dev/teogor/drifter/codegen/facades/Logger$Companion { + public static field instance Ldev/teogor/drifter/codegen/facades/Logger; + public final fun getInstance ()Ldev/teogor/drifter/codegen/facades/Logger; + public final fun setInstance (Ldev/teogor/drifter/codegen/facades/Logger;)V +} + +public final class dev/teogor/drifter/codegen/facades/LoggerKt { + public static final fun getLogger ()Ldev/teogor/drifter/codegen/facades/Logger; +} + +public final class dev/teogor/drifter/codegen/model/AdvancedMethodsData { + public fun (Ljava/lang/String;Ljava/util/List;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/util/List; + public final fun copy (Ljava/lang/String;Ljava/util/List;)Ldev/teogor/drifter/codegen/model/AdvancedMethodsData; + public static synthetic fun copy$default (Ldev/teogor/drifter/codegen/model/AdvancedMethodsData;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Ldev/teogor/drifter/codegen/model/AdvancedMethodsData; + public fun equals (Ljava/lang/Object;)Z + public final fun getName ()Ljava/lang/String; + public final fun getParams ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/model/BridgeKeyData { + public static final field Companion Ldev/teogor/drifter/codegen/model/BridgeKeyData$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lcom/squareup/kotlinpoet/TypeName; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;)Ldev/teogor/drifter/codegen/model/BridgeKeyData; + public static synthetic fun copy$default (Ldev/teogor/drifter/codegen/model/BridgeKeyData;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;ILjava/lang/Object;)Ldev/teogor/drifter/codegen/model/BridgeKeyData; + public fun equals (Ljava/lang/Object;)Z + public final fun getActualUnityNativeMethod ()Ljava/lang/String; + public final fun getKeyName ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getStorageKeyName ()Ljava/lang/String; + public final fun getStorageKeyValue ()Ljava/lang/String; + public final fun getType ()Lcom/squareup/kotlinpoet/TypeName; + public final fun getUnityNativeMethod ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/model/BridgeKeyData$Companion { + public final fun getNOT_PROVIDED ()Ldev/teogor/drifter/codegen/model/BridgeKeyData; +} + +public final class dev/teogor/drifter/codegen/model/CodeGenConfig { + public fun (ZZLjava/lang/String;)V + public final fun component1 ()Z + public final fun component2 ()Z + public final fun component3 ()Ljava/lang/String; + public final fun copy (ZZLjava/lang/String;)Ldev/teogor/drifter/codegen/model/CodeGenConfig; + public static synthetic fun copy$default (Ldev/teogor/drifter/codegen/model/CodeGenConfig;ZZLjava/lang/String;ILjava/lang/Object;)Ldev/teogor/drifter/codegen/model/CodeGenConfig; + public fun equals (Ljava/lang/Object;)Z + public final fun getAddDocumentation ()Z + public final fun getGenerateOperations ()Z + public final fun getGeneratedPackageName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/model/ConverterType { + public fun (Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;Lcom/squareup/kotlinpoet/TypeName;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Lcom/squareup/kotlinpoet/TypeName; + public final fun component4 ()Lcom/squareup/kotlinpoet/TypeName; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;Lcom/squareup/kotlinpoet/TypeName;)Ldev/teogor/drifter/codegen/model/ConverterType; + public static synthetic fun copy$default (Ldev/teogor/drifter/codegen/model/ConverterType;Ljava/lang/String;Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;Lcom/squareup/kotlinpoet/TypeName;ILjava/lang/Object;)Ldev/teogor/drifter/codegen/model/ConverterType; + public fun equals (Ljava/lang/Object;)Z + public final fun getName ()Ljava/lang/String; + public final fun getPackageName ()Ljava/lang/String; + public final fun getReceiverType ()Lcom/squareup/kotlinpoet/TypeName; + public final fun getReturnType ()Lcom/squareup/kotlinpoet/TypeName; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/model/DrifterActionBridgeData { + public static final field Companion Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/util/List; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData; + public static synthetic fun copy$default (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData; + public fun equals (Ljava/lang/Object;)Z + public final fun getBaseName ()Ljava/lang/String; + public final fun getExternalMethods ()Ljava/util/List; + public final fun getName ()Ljava/lang/String; + public final fun getPackageName ()Ljava/lang/String; + public final fun getParams ()Ljava/util/List; + public final fun getReceiverGameObject ()Ljava/lang/String; + public final fun getSimpleName ()Ljava/lang/String; + public fun hashCode ()I + public final fun isError ()Z + public fun toString ()Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/model/DrifterActionBridgeData$Companion { + public final fun getINVALID ()Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData; +} + +public abstract class dev/teogor/drifter/codegen/servicelocator/OutputWriter { + public fun (Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun addDocumentation (Lcom/squareup/kotlinpoet/FunSpec$Builder;Lcom/squareup/kotlinpoet/CodeBlock;)Lcom/squareup/kotlinpoet/FunSpec$Builder; + public final fun addDocumentation (Lcom/squareup/kotlinpoet/FunSpec$Builder;Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/FunSpec$Builder; + public final fun addDocumentation (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Lcom/squareup/kotlinpoet/CodeBlock;)Lcom/squareup/kotlinpoet/TypeSpec$Builder; + public final fun addDocumentation (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/TypeSpec$Builder; + public final fun getPackageName (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;)Ljava/lang/String; +} + +public final class dev/teogor/drifter/codegen/writers/ActionMappingsOutputWriter : dev/teogor/drifter/codegen/servicelocator/OutputWriter { + public fun (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun write (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;)Lcom/squareup/kotlinpoet/TypeName; +} + +public final class dev/teogor/drifter/codegen/writers/ActionParamsOutputWriter : dev/teogor/drifter/codegen/servicelocator/OutputWriter { + public fun (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun write (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;Ljava/util/List;)Lcom/squareup/kotlinpoet/TypeName; +} + +public final class dev/teogor/drifter/codegen/writers/KeyConstantsOutputWriter : dev/teogor/drifter/codegen/servicelocator/OutputWriter { + public fun (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun write (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;)Lcom/squareup/kotlinpoet/TypeName; +} + +public final class dev/teogor/drifter/codegen/writers/UnityMessageSenderOutputWriter : dev/teogor/drifter/codegen/servicelocator/OutputWriter { + public fun (Ldev/teogor/drifter/codegen/facades/CodeOutputStreamMaker;Ldev/teogor/drifter/codegen/model/CodeGenConfig;)V + public final fun write (Ldev/teogor/drifter/codegen/model/DrifterActionBridgeData;Lcom/squareup/kotlinpoet/TypeName;Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/TypeName; +} + diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts new file mode 100644 index 0000000..dfdc1ef --- /dev/null +++ b/codegen/build.gradle.kts @@ -0,0 +1,45 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + id("java-library") + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.winds) +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } +} + +dependencies { + api(projects.runtime) + + api(libs.kotlin.poet) + api(libs.kotlin.poet.ksp) +} + +winds { + mavenPublish { + displayName = "Codegen" + name = "codegen" + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/CodeGenerator.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/CodeGenerator.kt new file mode 100644 index 0000000..16df5a6 --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/CodeGenerator.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("ObjectPropertyName") + +package dev.teogor.drifter.codegen + +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.ConverterType +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.servicelocator.ServiceLocatorAccessor +import dev.teogor.drifter.codegen.servicelocator.actionMappingsOutputWriter +import dev.teogor.drifter.codegen.servicelocator.actionParamsOutputWriter +import dev.teogor.drifter.codegen.servicelocator.keyConstantsOutputWriter +import dev.teogor.drifter.codegen.servicelocator.unityMessageSenderOutputWriter + +class CodeGenerator( + override val codeOutputStreamMaker: CodeOutputStreamMaker, + override val codeGenConfig: CodeGenConfig, +) : ServiceLocatorAccessor { + + fun generate( + drifterActionBridges: List, + converters: List, + ) { + drifterActionBridges.filterNot { + it.isError + }.forEach { drifterActionBridge -> + keyConstantsOutputWriter.write(drifterActionBridge) + val actionMappings = actionMappingsOutputWriter.write(drifterActionBridge) + val actionParams = actionParamsOutputWriter.write( + drifterActionBridge, + converters, + ) + if (drifterActionBridge.receiverGameObject.isNotEmpty()) { + unityMessageSenderOutputWriter.write( + drifterActionBridge, + actionParams, + actionMappings, + ) + } + } + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Constants.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Constants.kt new file mode 100644 index 0000000..804f79a --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Constants.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.commons + +import com.squareup.kotlinpoet.ClassName + +val UnityMessageSender = ClassName( + "dev.teogor.drifter.common", + "UnityMessageSender", +) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Utils.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Utils.kt new file mode 100644 index 0000000..89f4e45 --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/commons/Utils.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.commons + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.ParameterizedTypeName +import com.squareup.kotlinpoet.TypeName +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.facades.writeTo +import java.util.Locale + +private val keywords: Set = setOf( + "in", + "is", +) + +fun String.sanitizePackageName(): String { + return split(".") + .joinToString(".") { if (keywords.contains(it)) "`$it`" else it } +} + +private val humps = "(?<=.)(?=\\p{Upper})".toRegex() + +fun String.toSnakeCase() = replace(humps, "_").uppercase(Locale.US) + +fun String.toTitleCase(reverse: Boolean = false): String { + val firstChar = first().let { + if (reverse) { + it.lowercaseChar() + } else { + it.uppercaseChar() + } + } + val rest = substring(1) + return "$firstChar$rest" +} + +inline fun fileBuilder( + packageName: String, + fileName: String, + crossinline block: FileSpec.Builder.() -> Unit, +) = FileSpec.builder( + packageName = packageName, + fileName = fileName, +).apply { + addFileComment( + """ + This file was automatically generated. Do not modify. + Auto-generated by Drifter. Developed by Teogor (Teodor Grigor) + """.trimIndent(), + ) +}.apply(block).build() + +fun FileSpec.writeWith( + codeOutputStreamMaker: CodeOutputStreamMaker, + error: (FileSpec) -> Unit = {}, +) { + try { + codeOutputStreamMaker.writeTo(this) + } catch (e: Exception) { + error(this) + } +} + +fun findCommonBase(string1: String, string2: String): String { + val parts1 = string1.split(".") + val parts2 = string2.split(".") + + val commonParts = mutableListOf() + + for (i in 0 until minOf(parts1.size, parts2.size)) { + if (parts1[i] == parts2[i]) { + commonParts.add(parts1[i]) + } else { + break + } + } + + return commonParts.joinToString(".") +} + +val TypeName.shortName: String + get() { + return when (this) { + is ClassName -> simpleName + is ParameterizedTypeName -> { + val rawType = rawType.shortName + val typeArguments = typeArguments.joinToString(", ", "<", ">") { it.shortName } + "$rawType$typeArguments" + } + else -> { + // Handle potential errors or unsupported types + println("Warning: Unsupported type encountered: $this") + toString() // Use the full type name as a fallback + } + } + } + +val TypeName.safe: TypeName + get() = copy(nullable = false) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/CodeOutputStreamMaker.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/CodeOutputStreamMaker.kt new file mode 100644 index 0000000..8267ceb --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/CodeOutputStreamMaker.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.facades + +import com.squareup.kotlinpoet.FileSpec +import java.io.OutputStream +import java.io.OutputStreamWriter +import java.nio.charset.StandardCharsets + +interface CodeOutputStreamMaker { + + fun makeFile( + name: String, + packageName: String, + vararg sourceIds: String, + ): OutputStream +} + +fun CodeOutputStreamMaker.writeTo( + file: FileSpec, + fileName: String = file.name, + packageName: String = file.packageName, +) { + makeFile( + fileName, + packageName, + ).use { out -> + OutputStreamWriter(out, StandardCharsets.UTF_8).use { writer -> + file.writeTo(writer) + } + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/Logger.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/Logger.kt new file mode 100644 index 0000000..501b70f --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/facades/Logger.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.facades + +interface Logger { + + fun logging(message: String) + + fun info(message: String) + + fun warn(message: String) + + fun error(message: String) + + fun exception(e: Throwable) + + companion object { + lateinit var instance: Logger + } +} + +val logger: Logger + get() = Logger.instance diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/AdvancedMethodsData.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/AdvancedMethodsData.kt new file mode 100644 index 0000000..e58cb85 --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/AdvancedMethodsData.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.model + +data class AdvancedMethodsData( + val name: String, + val params: List, +) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/BridgeKeyData.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/BridgeKeyData.kt new file mode 100644 index 0000000..479600c --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/BridgeKeyData.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.model + +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.UNIT +import dev.teogor.drifter.codegen.commons.toSnakeCase +import dev.teogor.drifter.codegen.commons.toTitleCase + +data class BridgeKeyData( + val name: String, + val keyName: String, + val unityNativeMethod: String, + val type: TypeName, +) { + + val storageKeyName: String + get() = keyName.ifEmpty { + name.toSnakeCase() + } + + val storageKeyValue: String + get() = keyName.ifEmpty { + name.toSnakeCase() + } + + val actualUnityNativeMethod: String + get() = unityNativeMethod.ifEmpty { + name.toTitleCase() + } + + companion object { + val NOT_PROVIDED = BridgeKeyData( + name = "", + keyName = "", + unityNativeMethod = "", + type = UNIT, + ) + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/CodeGenConfig.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/CodeGenConfig.kt new file mode 100644 index 0000000..91c9b36 --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/CodeGenConfig.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.model + +data class CodeGenConfig( + val addDocumentation: Boolean, + val generateOperations: Boolean, + val generatedPackageName: String?, +) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/ConverterType.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/ConverterType.kt new file mode 100644 index 0000000..7017dfc --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/ConverterType.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.model + +import com.squareup.kotlinpoet.TypeName + +data class ConverterType( + val name: String, + val packageName: String, + val receiverType: TypeName, + val returnType: TypeName, +) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/DrifterActionBridgeData.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/DrifterActionBridgeData.kt new file mode 100644 index 0000000..fa98bfd --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/model/DrifterActionBridgeData.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.model + +data class DrifterActionBridgeData( + val name: String, + val receiverGameObject: String, + val externalMethods: List?, + val simpleName: String, + val packageName: String, + val params: List, +) { + val isError: Boolean + get() = params.isEmpty() + + val baseName: String + get() = name.ifEmpty { simpleName } + + companion object { + val INVALID = DrifterActionBridgeData( + name = "", + receiverGameObject = "", + simpleName = "", + packageName = "", + params = emptyList(), + externalMethods = null, + ) + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/servicelocator/ServiceLocatorAccessor.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/servicelocator/ServiceLocatorAccessor.kt new file mode 100644 index 0000000..7fcd86f --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/servicelocator/ServiceLocatorAccessor.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.servicelocator + +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.TypeSpec +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.writers.ActionMappingsOutputWriter +import dev.teogor.drifter.codegen.writers.ActionParamsOutputWriter +import dev.teogor.drifter.codegen.writers.KeyConstantsOutputWriter +import dev.teogor.drifter.codegen.writers.UnityMessageSenderOutputWriter + +internal interface ServiceLocatorAccessor { + val codeOutputStreamMaker: CodeOutputStreamMaker + val codeGenConfig: CodeGenConfig +} + +abstract class OutputWriter( + private val codeGenConfig: CodeGenConfig, +) { + + fun DrifterActionBridgeData.getPackageName() = codeGenConfig.generatedPackageName ?: packageName + + fun FunSpec.Builder.addDocumentation( + format: String, + vararg args: Any, + ) = this.apply { + if (codeGenConfig.addDocumentation) { + addKdoc(format, args) + } + } + + fun FunSpec.Builder.addDocumentation( + block: CodeBlock, + ) = this.apply { + if (codeGenConfig.addDocumentation) { + addKdoc(block) + } + } + + fun TypeSpec.Builder.addDocumentation( + format: String, + vararg args: Any, + ) = this.apply { + if (codeGenConfig.addDocumentation) { + addKdoc(format, args) + } + } + + fun TypeSpec.Builder.addDocumentation( + block: CodeBlock, + ) = this.apply { + if (codeGenConfig.addDocumentation) { + addKdoc(block) + } + } +} + +internal val ServiceLocatorAccessor.keyConstantsOutputWriter + get() = KeyConstantsOutputWriter( + codeOutputStreamMaker, + codeGenConfig, + ) + +internal val ServiceLocatorAccessor.actionMappingsOutputWriter + get() = ActionMappingsOutputWriter( + codeOutputStreamMaker, + codeGenConfig, + ) + +internal val ServiceLocatorAccessor.actionParamsOutputWriter + get() = ActionParamsOutputWriter( + codeOutputStreamMaker, + codeGenConfig, + ) + +internal val ServiceLocatorAccessor.unityMessageSenderOutputWriter + get() = UnityMessageSenderOutputWriter( + codeOutputStreamMaker, + codeGenConfig, + ) diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionMappingsOutputWriter.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionMappingsOutputWriter.kt new file mode 100644 index 0000000..5d19a74 --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionMappingsOutputWriter.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.writers + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.TypeSpec +import dev.teogor.drifter.codegen.commons.fileBuilder +import dev.teogor.drifter.codegen.commons.toTitleCase +import dev.teogor.drifter.codegen.commons.writeWith +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.servicelocator.OutputWriter + +class ActionMappingsOutputWriter( + private val codeOutputStreamMaker: CodeOutputStreamMaker, + codeGenConfig: CodeGenConfig, +) : OutputWriter(codeGenConfig) { + + fun write(actionBridge: DrifterActionBridgeData): TypeName { + val name = "${actionBridge.baseName}ActionMappings" + fileBuilder( + packageName = actionBridge.getPackageName(), + fileName = name, + ) { + addType( + TypeSpec.objectBuilder(name) + .apply { + actionBridge.params.forEach { + addProperty( + PropertySpec.builder(it.name, String::class) + .addModifiers(KModifier.PUBLIC) + .addKdoc( + "Name of the corresponding Unity native method: `${it.actualUnityNativeMethod}`", + ) + .initializer("%S", it.actualUnityNativeMethod) + .build(), + ) + } + actionBridge.externalMethods?.let { methods -> + methods.forEach { + addProperty( + PropertySpec.builder(it.name, String::class) + .addModifiers(KModifier.PUBLIC) + .addKdoc( + "Name of the corresponding Unity native method: `${it.name.toTitleCase()}`", + ) + .initializer("%S", it.name.toTitleCase()) + .build(), + ) + } + } + } + .build(), + ) + }.writeWith(codeOutputStreamMaker) + + return ClassName( + packageName = actionBridge.getPackageName(), + name, + ) + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionParamsOutputWriter.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionParamsOutputWriter.kt new file mode 100644 index 0000000..5a11d2a --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/ActionParamsOutputWriter.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.writers + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.TypeSpec +import dev.teogor.drifter.codegen.commons.fileBuilder +import dev.teogor.drifter.codegen.commons.safe +import dev.teogor.drifter.codegen.commons.writeWith +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.ConverterType +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.servicelocator.OutputWriter + +class ActionParamsOutputWriter( + private val codeOutputStreamMaker: CodeOutputStreamMaker, + codeGenConfig: CodeGenConfig, +) : OutputWriter(codeGenConfig) { + + fun write(actionBridge: DrifterActionBridgeData, converters: List): TypeName { + val name = "${actionBridge.baseName}ActionParams" + val actualType = ClassName( + packageName = actionBridge.getPackageName(), + name, + ) + val jsonObject = ClassName( + packageName = "org.json", + "JSONObject", + ) + fileBuilder( + packageName = actionBridge.getPackageName(), + fileName = name, + ) { + addType( + TypeSpec.classBuilder(name) + .addModifiers(KModifier.DATA) + .addKdoc("Class to be used as parameter for when invoking unity native methods.") + .apply { + val flux = FunSpec.constructorBuilder() + actionBridge.params.forEach { + flux.addParameter( + ParameterSpec.builder(it.name, it.type.copy(nullable = true)) + .defaultValue("null") + .build(), + ) + addProperty( + PropertySpec.builder(it.name, it.type.copy(nullable = true)) + .initializer(it.name) + .build(), + ) + } + primaryConstructor(flux.build()) + } + .build(), + ) + + addFunction( + FunSpec.builder("toJsonObject") + .receiver(actualType) + .returns(jsonObject) + .addStatement("val json = %T()", jsonObject) + .apply { + actionBridge.params.forEach { + val converter = converters.firstOrNull { converterType -> + converterType.receiverType == it.type.safe + } + if (converter != null) { + val converterType = ClassName(converter.packageName, converter.name) + addStatement( + "${it.name}?.let { json.put(%S, it.%T()) }", + it.name, + converterType, + ) + } else { + addStatement("${it.name}?.let { json.put(%S, it) }", it.name) + } + } + } + .addStatement("return json") + .build(), + ) + }.writeWith(codeOutputStreamMaker) + + return ClassName( + packageName = actionBridge.getPackageName(), + name, + ) + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/KeyConstantsOutputWriter.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/KeyConstantsOutputWriter.kt new file mode 100644 index 0000000..45fb81a --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/KeyConstantsOutputWriter.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.writers + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.PropertySpec +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.TypeSpec +import dev.teogor.drifter.codegen.commons.fileBuilder +import dev.teogor.drifter.codegen.commons.writeWith +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.servicelocator.OutputWriter + +class KeyConstantsOutputWriter( + private val codeOutputStreamMaker: CodeOutputStreamMaker, + codeGenConfig: CodeGenConfig, +) : OutputWriter(codeGenConfig) { + + fun write(actionBridge: DrifterActionBridgeData): TypeName { + val name = "${actionBridge.baseName}KeyConstants" + fileBuilder( + packageName = actionBridge.getPackageName(), + fileName = name, + ) { + addType( + TypeSpec.objectBuilder(name) + .addKdoc( + "Used to change UnityPlayerPrefs. Recommended to use together with [UnityPlayerPrefs]", + ) + .apply { + actionBridge.params.forEach { + addProperty( + PropertySpec.builder(it.storageKeyName, String::class) + .addModifiers(KModifier.PUBLIC) + .initializer("%S", it.storageKeyValue) + .build(), + ) + } + } + .build(), + ) + }.writeWith(codeOutputStreamMaker) + + return ClassName( + packageName = actionBridge.getPackageName(), + name, + ) + } +} diff --git a/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/UnityMessageSenderOutputWriter.kt b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/UnityMessageSenderOutputWriter.kt new file mode 100644 index 0000000..e9b1f3a --- /dev/null +++ b/codegen/src/main/kotlin/dev/teogor/drifter/codegen/writers/UnityMessageSenderOutputWriter.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.codegen.writers + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.TypeSpec +import dev.teogor.drifter.codegen.commons.UnityMessageSender +import dev.teogor.drifter.codegen.commons.fileBuilder +import dev.teogor.drifter.codegen.commons.safe +import dev.teogor.drifter.codegen.commons.toTitleCase +import dev.teogor.drifter.codegen.commons.writeWith +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.codegen.model.CodeGenConfig +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.codegen.servicelocator.OutputWriter + +class UnityMessageSenderOutputWriter( + private val codeOutputStreamMaker: CodeOutputStreamMaker, + codeGenConfig: CodeGenConfig, +) : OutputWriter(codeGenConfig) { + + fun write( + actionBridge: DrifterActionBridgeData, + actionParams: TypeName, + actionMappings: TypeName, + ): TypeName { + val name = "${actionBridge.baseName}MessageSender" + fileBuilder( + packageName = actionBridge.getPackageName(), + fileName = name, + ) { + addType( + TypeSpec.classBuilder(name) + .superclass(UnityMessageSender) + .addSuperclassConstructorParameter( + "receiver = %S", + actionBridge.receiverGameObject, + ) + .apply { + actionBridge.params.forEach { param -> + addFunction( + FunSpec.builder(param.actualUnityNativeMethod.toTitleCase(true)) + .addCode( + CodeBlock.builder() + .apply { + addStatement("sendMessage(") + indent() + addStatement("%T.${param.name},", actionMappings) + addStatement( + "%T(", + actionParams, + ) + indent() + addStatement("${param.name} = ${param.name},") + unindent() + addStatement( + ").toJsonObject(),", + ) + unindent() + addStatement(")") + } + .build(), + ) + .addParameter( + ParameterSpec.builder(param.name, param.type.safe) + .build(), + ) + .build(), + ) + } + actionBridge.externalMethods?.let { methods -> + methods.forEach { method -> + addFunction( + FunSpec.builder(method.name.toTitleCase(true)) + .addCode( + CodeBlock.builder() + .apply { + addStatement("sendMessage(") + indent() + addStatement("%T.${method.name},", actionMappings) + addStatement( + "%T(", + actionParams, + ) + indent() + method.params.forEach { param -> + addStatement("$param = $param,") + } + unindent() + addStatement( + ").toJsonObject(),", + ) + unindent() + addStatement(")") + } + .build(), + ) + .apply { + method.params.forEach { param -> + val type = actionBridge.params.firstOrNull { + it.name == param + } ?: error("Please provide a valid name: $param") + addParameter( + ParameterSpec.builder(type.name, type.type.safe) + .build(), + ) + } + } + .build(), + ) + } + } + } + .build(), + ) + }.writeWith(codeOutputStreamMaker) + + return ClassName( + packageName = actionBridge.getPackageName(), + name, + ) + } +} diff --git a/drifter-plugin/settings.gradle.kts b/drifter-plugin/settings.gradle.kts index 204e218..0a2e8c4 100644 --- a/drifter-plugin/settings.gradle.kts +++ b/drifter-plugin/settings.gradle.kts @@ -35,3 +35,5 @@ dependencyResolutionManagement { } } } + +include(":gradle-plugin-api") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dd1dd13..65f2e74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,8 @@ androidDesugarJdkLibs = "2.0.3" # @todo-ceres required by id::ceres.android.application.compose -androidxComposeCompiler = "1.5.3" -androidxComposeBom = "2023.09.01" +androidxComposeCompiler = "1.5.8" +androidxComposeBom = "2024.02.01" # @todo-ceres required by id::ceres.android.application.firebase firebaseBom = "32.3.1" @@ -30,7 +30,6 @@ protobufPlugin = "0.9.4" api-validator = "0.13.2" annotation = "1.7.0" buildConfig = "3.1.0" -kotlin = "1.9.10" core-ktx = "1.12.0" junit = "4.13.2" androidx-test-ext-junit = "1.1.5" @@ -39,7 +38,8 @@ lifecycle-runtime-ktx = "2.6.2" activity-compose = "1.7.2" compose-bom = "2023.09.01" appcompat = "1.6.1" -ksp = "1.9.0-1.0.13" +kotlin = "1.9.22" +ksp = "1.9.22-1.0.17" material = "1.9.0" startup-runtime = "1.2.0-alpha02" gson = "2.10.1" @@ -47,6 +47,7 @@ vanniktechMavenPlugin = "0.25.3" dokka = "1.9.0" spotless = "6.21.0" winds = "1.0.0-beta02" +kotlin-poet = "1.14.2" [libraries] # @todo-ceres required by id::ceres.android.application @@ -102,6 +103,9 @@ ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } material3 = { group = "androidx.compose.material3", name = "material3" } appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } +kotlin-poet = { module = "com.squareup:kotlinpoet", version.ref = "kotlin-poet" } +kotlin-poet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin-poet" } +ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } diff --git a/ksp/.gitignore b/ksp/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/ksp/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ksp/api/ksp.api b/ksp/api/ksp.api new file mode 100644 index 0000000..9ad483a --- /dev/null +++ b/ksp/api/ksp.api @@ -0,0 +1,51 @@ +public final class dev/teogor/drifter/ksp/ProcessorProvider : com/google/devtools/ksp/processing/SymbolProcessorProvider { + public fun ()V + public fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lcom/google/devtools/ksp/processing/SymbolProcessor; +} + +public final class dev/teogor/drifter/ksp/codegen/KspCodeOutputStreamMaker : dev/teogor/drifter/codegen/facades/CodeOutputStreamMaker { + public fun (Lcom/google/devtools/ksp/processing/CodeGenerator;Ldev/teogor/drifter/ksp/commons/KSFileSourceMapper;)V + public fun makeFile (Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)Ljava/io/OutputStream; +} + +public final class dev/teogor/drifter/ksp/codegen/KspLogger : dev/teogor/drifter/codegen/facades/Logger { + public fun (Lcom/google/devtools/ksp/processing/KSPLogger;)V + public fun error (Ljava/lang/String;)V + public fun exception (Ljava/lang/Throwable;)V + public fun info (Ljava/lang/String;)V + public fun logging (Ljava/lang/String;)V + public fun warn (Ljava/lang/String;)V +} + +public abstract interface class dev/teogor/drifter/ksp/commons/KSFileSourceMapper { + public abstract fun mapToKSFile (Ljava/lang/String;)Lcom/google/devtools/ksp/symbol/KSFile; +} + +public final class dev/teogor/drifter/ksp/processors/ConfigParser { + public static final field Companion Ldev/teogor/drifter/ksp/processors/ConfigParser$Companion; + public fun (Ljava/util/Map;)V + public final fun parse ()Ldev/teogor/drifter/codegen/model/CodeGenConfig; +} + +public final class dev/teogor/drifter/ksp/processors/ConfigParser$Companion { +} + +public final class dev/teogor/drifter/ksp/processors/KspToCodeGenDestinationsMapper : dev/teogor/drifter/ksp/commons/KSFileSourceMapper { + public fun (Lcom/google/devtools/ksp/processing/Resolver;)V + public fun mapToKSFile (Ljava/lang/String;)Lcom/google/devtools/ksp/symbol/KSFile; +} + +public final class dev/teogor/drifter/ksp/processors/Processor : com/google/devtools/ksp/processing/SymbolProcessor { + public fun (Lcom/google/devtools/ksp/processing/CodeGenerator;Lcom/google/devtools/ksp/processing/KSPLogger;Ljava/util/Map;)V + public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List; +} + +public final class dev/teogor/drifter/ksp/processors/ProcessorKt { + public static final fun isDataClass (Lcom/google/devtools/ksp/symbol/KSClassDeclaration;)Z +} + +public final class dev/teogor/drifter/ksp/processors/WrongConfigurationSetup : java/lang/RuntimeException { + public fun (Ljava/lang/String;Ljava/lang/Throwable;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + diff --git a/ksp/build.gradle.kts b/ksp/build.gradle.kts new file mode 100644 index 0000000..ae96b6a --- /dev/null +++ b/ksp/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.winds) +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType().configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } +} + +dependencies { + api(projects.runtime) + implementation(projects.codegen) + + implementation(libs.ksp.api) +} + +winds { + mavenPublish { + displayName = "KSP" + name = "ksp" + } +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/ProcessorProvider.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/ProcessorProvider.kt new file mode 100644 index 0000000..a8369c2 --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/ProcessorProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider +import dev.teogor.drifter.ksp.processors.Processor + +class ProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return Processor( + environment.codeGenerator, + environment.logger, + environment.options, + ) + } +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspCodeOutputStreamMaker.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspCodeOutputStreamMaker.kt new file mode 100644 index 0000000..49fe530 --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspCodeOutputStreamMaker.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp.codegen + +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.Dependencies +import dev.teogor.drifter.codegen.facades.CodeOutputStreamMaker +import dev.teogor.drifter.ksp.commons.KSFileSourceMapper +import java.io.OutputStream + +class KspCodeOutputStreamMaker( + private val codeGenerator: CodeGenerator, + private val sourceMapper: KSFileSourceMapper, +) : CodeOutputStreamMaker { + + override fun makeFile( + name: String, + packageName: String, + vararg sourceIds: String, + ): OutputStream { + val dependencies = if (sourceIds.isEmpty()) { + Dependencies.ALL_FILES + } else { + Dependencies( + true, + *sourceIds.mapNotNull { sourceMapper.mapToKSFile(it) }.toTypedArray(), + ) + } + + return codeGenerator.createNewFile( + dependencies = dependencies, + fileName = name, + packageName = packageName, + ) + } +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspLogger.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspLogger.kt new file mode 100644 index 0000000..5a76c46 --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/codegen/KspLogger.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp.codegen + +import com.google.devtools.ksp.processing.KSPLogger +import dev.teogor.drifter.codegen.facades.Logger + +class KspLogger( + private val kspLogger: KSPLogger, +) : Logger { + + override fun logging(message: String) = kspLogger.logging(message) + + override fun info(message: String) = kspLogger.info(message) + + override fun warn(message: String) = kspLogger.warn(message) + + override fun error(message: String) = kspLogger.error(message) + + override fun exception(e: Throwable) = kspLogger.exception(e) +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/commons/KSFileSourceMapper.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/commons/KSFileSourceMapper.kt new file mode 100644 index 0000000..feaed84 --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/commons/KSFileSourceMapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp.commons + +import com.google.devtools.ksp.symbol.KSFile + +fun interface KSFileSourceMapper { + + fun mapToKSFile(sourceId: String): KSFile? +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/ConfigParser.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/ConfigParser.kt new file mode 100644 index 0000000..93d8ceb --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/ConfigParser.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("SameParameterValue") + +package dev.teogor.drifter.ksp.processors + +import dev.teogor.drifter.codegen.model.CodeGenConfig + +class ConfigParser( + private val options: Map, +) { + + companion object { + private const val PREFIX = "drifter" + + // Configs + private const val ADD_DOCUMENTATION = "$PREFIX.addDocumentation" + private const val GENERATE_OPERATIONS = "$PREFIX.generateOperations" + private const val GENERATED_PACKAGE_NAME = "$PREFIX.generatedPackageName" + } + + fun parse(): CodeGenConfig { + val addDocumentation = parseBoolean(ADD_DOCUMENTATION) ?: true + val generateOperations = parseBoolean(GENERATE_OPERATIONS) ?: true + val generatedPackageName = options[GENERATED_PACKAGE_NAME]?.trim()?.removeSuffix(".") + + return CodeGenConfig( + addDocumentation = addDocumentation, + generateOperations = generateOperations, + generatedPackageName = generatedPackageName, + ) + } + + private fun parseBoolean(key: String): Boolean? { + return options[key]?.runCatching { + toBooleanStrict() + }?.getOrElse { + throw WrongConfigurationSetup("$key must be a boolean value!", cause = it) + } + } +} + +class WrongConfigurationSetup(message: String, cause: Throwable? = null) : + RuntimeException(message, cause) diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/KspToCodeGenDestinationsMapper.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/KspToCodeGenDestinationsMapper.kt new file mode 100644 index 0000000..e857cad --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/KspToCodeGenDestinationsMapper.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp.processors + +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.KSFile +import dev.teogor.drifter.ksp.commons.KSFileSourceMapper + +class KspToCodeGenDestinationsMapper( + private val resolver: Resolver, +) : KSFileSourceMapper { + private val sourceFilesById = mutableMapOf() + + override fun mapToKSFile(sourceId: String): KSFile? { + return sourceFilesById[sourceId] + } +} diff --git a/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/Processor.kt b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/Processor.kt new file mode 100644 index 0000000..1daf8d8 --- /dev/null +++ b/ksp/src/main/kotlin/dev/teogor/drifter/ksp/processors/Processor.kt @@ -0,0 +1,178 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter.ksp.processors + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.getDeclaredFunctions +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.ClassKind +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.Modifier +import com.squareup.kotlinpoet.UNIT +import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.toTypeName +import dev.teogor.drifter.DrifterEncoder +import dev.teogor.drifter.DrifterMappingKey +import dev.teogor.drifter.DrifterModule +import dev.teogor.drifter.DrifterUnityMethod +import dev.teogor.drifter.codegen.CodeGenerator +import dev.teogor.drifter.codegen.facades.Logger +import dev.teogor.drifter.codegen.model.AdvancedMethodsData +import dev.teogor.drifter.codegen.model.BridgeKeyData +import dev.teogor.drifter.codegen.model.ConverterType +import dev.teogor.drifter.codegen.model.DrifterActionBridgeData +import dev.teogor.drifter.ksp.codegen.KspCodeOutputStreamMaker +import dev.teogor.drifter.ksp.codegen.KspLogger +import kotlin.reflect.KClass + +class Processor( + private val codeGenerator: KSPCodeGenerator, + private val logger: KSPLogger, + private val options: Map, +) : SymbolProcessor { + + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + Logger.instance = KspLogger(logger) + + val annotatedDrifterBridges = resolver.getDrifterActionBridges() + val annotatedDrifterConverters = resolver.getDrifterConverters() + + if ( + !annotatedDrifterBridges.iterator().hasNext() && + !annotatedDrifterConverters.iterator().hasNext() + ) { + return emptyList() + } + + val sourceFiles = annotatedDrifterBridges.mapNotNull { + it.containingFile + } + annotatedDrifterConverters.mapNotNull { + it.containingFile + } + + val drifterActionBridges = annotatedDrifterBridges.map { kClass -> + if (kClass.isDataClass) { + val drifterModule = kClass.getAnnotationsByType( + DrifterModule::class, + ).first() + val parameters = kClass.primaryConstructor!!.parameters.map { param -> + val drifterMappingKey = param.getAnnotationsByType(DrifterMappingKey::class).firstOrNull() + drifterMappingKey?.let { + BridgeKeyData( + name = param.name!!.asString(), + keyName = it.storageKey, + unityNativeMethod = it.exposedMethod, + type = param.type.toTypeName(), + ) + } ?: BridgeKeyData.NOT_PROVIDED.copy( + name = param.name!!.asString(), + type = param.type.toTypeName(), + ) + } + val ksTypeRef = kClass.annotations.first { + it.annotationType.resolve().toClassName() == DrifterModule::class.asClassName() + }.let { dab -> + val type = dab.arguments.firstOrNull { + it.name!!.asString() == "methods" + }?.value as KSType + type.declaration as KSClassDeclaration + } + val externalMethods = if (ksTypeRef.toClassName() != UNIT) { + ksTypeRef.getDeclaredFunctions() + .map { + it to it.getAnnotationsByType(DrifterUnityMethod::class).firstOrNull() + } + .filterNot { it.second == null } + .map { (kFun, param) -> + val name = param!!.name + AdvancedMethodsData( + name = name, + params = param.parameters.toList(), + ) + }.toList() + } else { + null + } + DrifterActionBridgeData( + name = drifterModule.name, + receiverGameObject = drifterModule.receiver, + externalMethods = externalMethods, + simpleName = kClass.simpleName.asString(), + packageName = kClass.packageName.asString(), + params = parameters, + ) + } else { + Logger.instance.error("Class '${kClass.simpleName}' is not a data class.") + DrifterActionBridgeData.INVALID + } + }.toList() + + val converters = annotatedDrifterConverters.filter { + it.returnType != null && + it.extensionReceiver != null + }.map { kFun -> + val returnType = kFun.returnType!!.toTypeName() + val receiverType = kFun.extensionReceiver!!.toTypeName() + ConverterType( + name = kFun.simpleName.asString(), + packageName = kFun.packageName.asString(), + returnType = returnType, + receiverType = receiverType, + ) + }.toList() + + CodeGenerator( + codeOutputStreamMaker = KspCodeOutputStreamMaker( + codeGenerator = codeGenerator, + sourceMapper = KspToCodeGenDestinationsMapper(resolver), + ), + codeGenConfig = ConfigParser(options).parse(), + ).generate( + drifterActionBridges = drifterActionBridges, + converters = converters, + ) + + return emptyList() + } + + private fun Resolver.findAnnotations( + kClass: KClass<*>, + ) = getSymbolsWithAnnotation( + kClass.qualifiedName.toString(), + ) + + private fun Resolver.getDrifterActionBridges(): Sequence { + return findAnnotations(DrifterModule::class).filterIsInstance() + } + + private fun Resolver.getDrifterConverters(): Sequence { + return findAnnotations(DrifterEncoder::class).filterIsInstance() + } +} + +val KSClassDeclaration.isDataClass: Boolean + get() = classKind == ClassKind.CLASS && modifiers.contains(Modifier.DATA) + +typealias KSPCodeGenerator = com.google.devtools.ksp.processing.CodeGenerator diff --git a/ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 0000000..9b2dfe4 --- /dev/null +++ b/ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +dev.teogor.drifter.ksp.ProcessorProvider diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/runtime/api/runtime.api b/runtime/api/runtime.api new file mode 100644 index 0000000..8cf3e87 --- /dev/null +++ b/runtime/api/runtime.api @@ -0,0 +1,19 @@ +public abstract interface annotation class dev/teogor/drifter/DrifterEncoder : java/lang/annotation/Annotation { +} + +public abstract interface annotation class dev/teogor/drifter/DrifterMappingKey : java/lang/annotation/Annotation { + public abstract fun exposedMethod ()Ljava/lang/String; + public abstract fun storageKey ()Ljava/lang/String; +} + +public abstract interface annotation class dev/teogor/drifter/DrifterModule : java/lang/annotation/Annotation { + public abstract fun methods ()Ljava/lang/Class; + public abstract fun name ()Ljava/lang/String; + public abstract fun receiver ()Ljava/lang/String; +} + +public abstract interface annotation class dev/teogor/drifter/DrifterUnityMethod : java/lang/annotation/Annotation { + public abstract fun name ()Ljava/lang/String; + public abstract fun parameters ()[Ljava/lang/String; +} + diff --git a/runtime/build.gradle.kts b/runtime/build.gradle.kts new file mode 100644 index 0000000..e5418c4 --- /dev/null +++ b/runtime/build.gradle.kts @@ -0,0 +1,39 @@ +/* + * Copyright 2023 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("java-library") + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.winds) +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } +} + +winds { + mavenPublish { + displayName = "Runtime" + name = "runtime" + } +} diff --git a/runtime/src/main/kotlin/dev/teogor/drifter/DrifterEncoder.kt b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterEncoder.kt new file mode 100644 index 0000000..2fffe79 --- /dev/null +++ b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterEncoder.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter + +/** + * Marks a function as a custom encoder for a specific type during JSON serialization. + * + * This annotation allows you to define custom logic for converting objects of a specific + * type into a format suitable for JSON. This is useful when the default serialization + * behavior doesn't meet your needs or requires specific transformations. + * + * The annotated function takes an object of the specified type as input and returns a + * JSON-compatible value. + * + * **Example Usage:** + * + * ```kotlin + * @DrifterEncoder + * fun Color.encodeFromColor(): Int = toArgb() + * + * // ... in a serialization method + * + * val json = JSONObject() + * waterColor?.let { json.put("waterColor", it.encodeFromColor()) } + * ``` + */ +@Target(AnnotationTarget.FUNCTION) +annotation class DrifterEncoder diff --git a/runtime/src/main/kotlin/dev/teogor/drifter/DrifterMappingKey.kt b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterMappingKey.kt new file mode 100644 index 0000000..9e99be2 --- /dev/null +++ b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterMappingKey.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter + +/** + * Specifies a key for mapping a property or parameter to a Unity method. + * + * @property storageKey The key used for storage and retrieval. If empty, defaults to the + * property/parameter name in snake_case uppercase (e.g., "waterColor" -> "WATER_COLOR"). + * @property exposedMethod The name of the Unity method associated with this property/parameter. + * **Example Usage:** + * + * ```kotlin + * @DrifterMappingKey( + * storageKey = "WATER_COLOR", + * exposedMethod = "SetEditorColor" + * ) + * val waterColor: Color? = null + * ``` + */ +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER) +annotation class DrifterMappingKey( + val storageKey: String = "", + val exposedMethod: String = "", +) diff --git a/runtime/src/main/kotlin/dev/teogor/drifter/DrifterModule.kt b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterModule.kt new file mode 100644 index 0000000..58fed9f --- /dev/null +++ b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterModule.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter + +import kotlin.reflect.KClass + +/** + * Marks a class as a Drifter module, defining its functionality and integration points. + * + * This annotation associates a class with a specific Unity GameObject and exposes its methods + * for communication and data exchange with the Unity environment. + * + * @property name The name of the module, used for identification and logging. + * @property receiver The name of the Unity GameObject this module interacts with. + * @property methods The Kotlin class representing the interface containing exposed methods. + * Defaults to [Unit] if no methods are explicitly defined. + * + * **Example Usage:** + * + * ```kotlin + * @DrifterModule( + * name = "Aquarium", + * receiver = "BridgeController", + * methods = AquariumMethods::class + * ) + * data class AquariumConfig( + * val isEditorMode: Boolean? = null, + * val waterColor: Color? = null, + * // ... other properties + * ) + * + * interface AquariumMethods { + * @DrifterUnityMethod + * fun setEditorMode() + * // ... other methods + * } + * ``` + */ +@Target(AnnotationTarget.CLASS) +annotation class DrifterModule( + val name: String = "", + val receiver: String = "", + val methods: KClass<*> = Unit::class, +) diff --git a/runtime/src/main/kotlin/dev/teogor/drifter/DrifterUnityMethod.kt b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterUnityMethod.kt new file mode 100644 index 0000000..1038bfb --- /dev/null +++ b/runtime/src/main/kotlin/dev/teogor/drifter/DrifterUnityMethod.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2024 teogor (Teodor Grigor) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.teogor.drifter + +/** + * Marks a function as a method exposed to the Unity environment. + * + * This annotation allows you to declare functions within your Kotlin code that can be + * accessed and called from Unity scripts. It specifies the name of the exposed method + * in Unity and the names of its parameters. + * + * @property name The name of the exposed method in Unity. Defaults to the function name with + * first letter uppercase. + * @property parameters An array of parameter names expected by the exposed method in Unity. + * + * **Example Usage:** + * + * ```kotlin + * @DrifterUnityMethod( + * name = "SetEditorMode", + * parameters = ["isEditorMode", "animated"] + * ) + * fun setEditorMode() + * ``` + */ +@Target(AnnotationTarget.FUNCTION) +annotation class DrifterUnityMethod( + val name: String = "", + val parameters: Array = [], +) diff --git a/settings.gradle.kts b/settings.gradle.kts index 389d86a..66fbd66 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,3 +34,6 @@ include(":drifter-core") include(":drifter-integration") include(":drifter-plugin") include(":drifter-wallpaper") +include(":codegen") +include(":ksp") +include(":runtime")