From ba7a056ed594322133f989d91996a57b7d6c96ad Mon Sep 17 00:00:00 2001 From: Emanuele Date: Thu, 22 Sep 2022 17:02:25 +0200 Subject: [PATCH 01/16] Update README with Xcode integration information Might close #44 --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 28b3da5..246a976 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,41 @@ dependencies { # Usage +## Xcode configuration + +The Swift code generated from this plugin is not automatically included in the shared framework you might have. + +You have 2 options to use it in your iOS project: +- Xcode direct file integration +- CocoaPods integration + +### Xcode direct file integration + +You can directly import the generated file in your Xcode project like it's a file you have written on your own. + +To do so: +- open the Xcode project +- right click on "iosApp" +- choose "Add files to iOSApp" +- add the file from the generated folder (you might need to read the FAQ to know where the generated folder is) +- you are now good to go! + +### CocoaPods integration + +After you have added the moko-kswift plugin to your shared module and synced your project, a new Gradle task should appear with name `kSwiftXXXXXPodspec` where `XXXXX` is the name of your shared module (so your task might be named `kSwiftsharedPodspec`). + +- Run the task doing `./gradlew kSwiftsharedPodspec` from the root of your project. + This will generate a new podspec file, `XXXXXSwift.podspec`, where `XXXXX` is still the name of your shared module (so e.g. `sharedSwift.podspec`) + +- Now edit the `Podfile` inside the iOS project adding this line + `pod 'sharedSwift', :path => '../shared'` + just after the one already there for the already available shared module + `pod 'shared', :path => '../shared'` + +- Now run `pod install` from the `iosApp` folder so the new framework is linked to your project. + +- Whenever you need a Swift file generated from moko-kswift just import the generated module (e.g. `import sharedSwift`) and you are good to go! + ## Sealed classes/interfaces to Swift enum Enable feature in project `build.gradle`: From 2aa674d4ff3b83510f4e3149a997c3bbdc74a0c0 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 12 Feb 2023 19:33:11 -0800 Subject: [PATCH 02/16] Add a feature to generate Swift enums with associated values --- build.gradle.kts | 1 + gradle/libs.versions.toml | 4 +- kswift-gradle-plugin/settings.gradle.kts | 3 +- .../SealedToSwiftAssociatedEnumFeature.kt | 864 ++++++++++++++++++ .../moko/kswift/plugin/model/TestInstances.kt | 11 + .../moko/kswift/plugin/model/TestingSealed.kt | 75 ++ 6 files changed, 955 insertions(+), 3 deletions(-) create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt create mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt create mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt diff --git a/build.gradle.kts b/build.gradle.kts index 7f35a77..93cc216 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ buildscript { mavenCentral() google() gradlePluginPortal() + maven("https://jitpack.io") } dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 44f9be8..4a5ccbf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mokoResourcesVersion = "0.16.2" kotlinxMetadataKLibVersion = "0.0.1" kotlinPoetVersion = "1.6.0" -swiftPoetVersion = "1.3.1" +swiftPoetVersion = "1.5.1.0" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } @@ -30,7 +30,7 @@ mokoMvvmState = { module = "dev.icerock.moko:mvvm-state" , version.ref = "mokoMv mokoResources = { module = "dev.icerock.moko:resources" , version.ref = "mokoResourcesVersion" } kotlinPoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoetVersion" } -swiftPoet = { module = "io.outfoxx:swiftpoet", version.ref = "swiftPoetVersion" } +swiftPoet = { module = "com.github.hbmartin:swiftpoet", version.ref = "swiftPoetVersion" } kotlinCompilerEmbeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlinVersion" } kotlinxMetadataKLib = { module = "org.jetbrains.kotlinx:kotlinx-metadata-klib", version.ref = "kotlinxMetadataKLibVersion" } diff --git a/kswift-gradle-plugin/settings.gradle.kts b/kswift-gradle-plugin/settings.gradle.kts index ad522e0..e3bd01b 100644 --- a/kswift-gradle-plugin/settings.gradle.kts +++ b/kswift-gradle-plugin/settings.gradle.kts @@ -10,8 +10,8 @@ pluginManagement { repositories { mavenCentral() google() - gradlePluginPortal() + maven("https://jitpack.io") } resolutionStrategy { @@ -28,6 +28,7 @@ dependencyResolutionManagement { repositories { mavenCentral() google() + maven("https://jitpack.io") } versionCatalogs { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt new file mode 100644 index 0000000..c7573ca --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -0,0 +1,864 @@ +package dev.icerock.moko.kswift.plugin.feature + +import dev.icerock.moko.kswift.plugin.buildTypeVariableNames +import dev.icerock.moko.kswift.plugin.context.ClassContext +import dev.icerock.moko.kswift.plugin.context.kLibClasses +import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics +import dev.icerock.moko.kswift.plugin.getSimpleName +import dev.icerock.moko.kswift.plugin.objcNameToSwift +import io.outfoxx.swiftpoet.ANY_OBJECT +import io.outfoxx.swiftpoet.ARRAY +import io.outfoxx.swiftpoet.BOOL +import io.outfoxx.swiftpoet.CodeBlock +import io.outfoxx.swiftpoet.DICTIONARY +import io.outfoxx.swiftpoet.DeclaredTypeName +import io.outfoxx.swiftpoet.EnumerationCaseSpec +import io.outfoxx.swiftpoet.FLOAT32 +import io.outfoxx.swiftpoet.FLOAT64 +import io.outfoxx.swiftpoet.FunctionSpec +import io.outfoxx.swiftpoet.FunctionTypeName +import io.outfoxx.swiftpoet.INT16 +import io.outfoxx.swiftpoet.INT32 +import io.outfoxx.swiftpoet.INT64 +import io.outfoxx.swiftpoet.INT8 +import io.outfoxx.swiftpoet.Modifier +import io.outfoxx.swiftpoet.ParameterSpec +import io.outfoxx.swiftpoet.ParameterizedTypeName +import io.outfoxx.swiftpoet.PropertySpec +import io.outfoxx.swiftpoet.SET +import io.outfoxx.swiftpoet.STRING +import io.outfoxx.swiftpoet.TupleTypeName +import io.outfoxx.swiftpoet.TypeName +import io.outfoxx.swiftpoet.TypeSpec +import io.outfoxx.swiftpoet.TypeVariableName +import io.outfoxx.swiftpoet.UIN16 +import io.outfoxx.swiftpoet.UINT32 +import io.outfoxx.swiftpoet.UINT64 +import io.outfoxx.swiftpoet.UINT8 +import io.outfoxx.swiftpoet.VOID +import io.outfoxx.swiftpoet.parameterizedBy +import kotlinx.metadata.ClassName +import kotlinx.metadata.Flag +import kotlinx.metadata.KmClass +import kotlinx.metadata.KmClassifier +import kotlinx.metadata.KmType +import kotlinx.metadata.KmTypeProjection +import org.gradle.configurationcache.extensions.capitalized +import java.util.Locale +import kotlin.reflect.KClass + +class SealedToSwiftAssociatedEnumFeature( + override val featureContext: KClass, + override val filter: Filter, +) : ProcessorFeature() { + + @Suppress("ReturnCount") + override fun doProcess(featureContext: ClassContext, processorContext: ProcessorContext) { + if (featureContext.clazz.sealedSubclasses.isEmpty()) return + + val kotlinFrameworkName: String = processorContext.framework.baseName + val kmClass: KmClass = featureContext.clazz + + if (Flag.IS_PUBLIC(kmClass.flags).not()) return + + val originalClassName: String = getSimpleName(kmClass.name, featureContext.kLibClasses) + + println("Generating enum for sealed class $originalClassName") + + val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) + if (sealedCases.isEmpty()) return + + val typeVariables: List = + kmClass.buildTypeVariableNames(kotlinFrameworkName) + + val className: String = originalClassName.replace(".", "").plus("Ks") + val enumType: TypeSpec = TypeSpec.enumBuilder(className) + .addDoc("selector: ${featureContext.prefixedUniqueId}") + .apply { + typeVariables.forEach { addTypeVariable(it) } + sealedCases.forEach { addEnumCase(it.enumCaseSpec) } + } + .addModifiers(Modifier.PUBLIC) + .addFunction( + buildEnumConstructor( + featureContext = featureContext, + kotlinFrameworkName = kotlinFrameworkName, + sealedCases = sealedCases, + className = className, + originalClassName = originalClassName, + ), + ) + .addProperty( + buildSealedProperty( + featureContext = featureContext, + kotlinFrameworkName = kotlinFrameworkName, + sealedCases = sealedCases, + ), + ) + .build() + + processorContext.fileSpecBuilder.addType(enumType) + } + + private fun buildEnumConstructor( + featureContext: ClassContext, + kotlinFrameworkName: String, + sealedCases: List, + className: String, + originalClassName: String, + ): FunctionSpec { + return FunctionSpec.builder("init") + .addModifiers(Modifier.PUBLIC) + .addParameter( + label = "_", + name = "obj", + type = featureContext.clazz.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ), + ) + .addCode( + CodeBlock.builder() + .apply { + sealedCases.forEachIndexed { index, enumCase -> + buildString { + if (index != 0) append("} else ") + append("if ") + append(enumCase.initCheck) + append(" {") + append('\n') + }.also { add(it) } + indent() + buildString { + append("self = .") + append(enumCase.name) + append(enumCase.initBlock) + append('\n') + }.also { add(it) } + unindent() + } + add("} else {\n") + indent() + add( + "fatalError(\"$className not synchronized with $originalClassName class\")\n", + ) + unindent() + add("}\n") + } + .build(), + ) + .build() + } + + private fun buildEnumCases( + kotlinFrameworkName: String, + featureContext: ClassContext, + ): List { + val kmClass = featureContext.clazz + return kmClass.sealedSubclasses.mapNotNull { sealedClassName -> + val sealedClass: KmClass = featureContext.parentContext + .fragment.classes.first { it.name == sealedClassName } + + if (Flag.IS_PUBLIC(sealedClass.flags).not()) return@mapNotNull null + + buildEnumCase( + kotlinFrameworkName = kotlinFrameworkName, + featureContext = featureContext, + subclassName = sealedClassName, + sealedCaseClass = sealedClass, + ) + } + } + + private fun buildEnumCase( + kotlinFrameworkName: String, + featureContext: ClassContext, + subclassName: ClassName, + sealedCaseClass: KmClass, + ): EnumCase { + val kmClass = featureContext.clazz + val name: String = if (subclassName.startsWith(kmClass.name)) { + subclassName.removePrefix(kmClass.name).removePrefix(".") + } else { + subclassName.removePrefix(kmClass.name.substringBeforeLast("/")).removePrefix("/") + } + val decapitalizedName: String = name.decapitalize(Locale.ROOT) + + val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) + val caseArg = sealedCaseClass.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ) + + return EnumCase( + name = decapitalizedName, + param = if (isObject) null else caseArg, + initCheck = if (isObject) { + "obj is $caseArg" + } else { + "let obj = obj as? $caseArg" + }, + caseArg = caseArg, + isObject = isObject, + explodedParams = sealedCaseClass.constructors.first().valueParameters.map { + Pair( + it.name, + it.type?.kotlinPrimitiveTypeNameToSwift(kotlinFrameworkName) + ?: DeclaredTypeName.typeName("Swift.FailedToGetReturnType"), + ) + }, + ) + } + + private fun buildSealedProperty( + featureContext: ClassContext, + kotlinFrameworkName: String, + sealedCases: List, + ): PropertySpec { + val returnType: TypeName = featureContext.clazz.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ) + return PropertySpec.builder("sealed", type = returnType) + .addModifiers(Modifier.PUBLIC) + .getter( + FunctionSpec + .getterBuilder() + .addCode(buildSealedPropertyBody(sealedCases)) + .build(), + ).build() + } + + private fun buildSealedPropertyBody( + sealedCases: List, + ): CodeBlock = CodeBlock.builder().apply { + add("switch self {\n") + sealedCases.forEach { enumCase -> + buildString { + append("case .") + append(enumCase.name) + append(enumCase.caseBlock) + append(":\n") + }.also { add(it) } + indent() + addSealedCaseReturnCode(enumCase) + unindent() + } + add("}\n") + }.build() + + private fun CodeBlock.Builder.addSealedCaseReturnCode( + enumCase: EnumCase, + ) { + val parameters = enumCase.swiftToKotlinConstructor() + add("return ${enumCase.name.capitalized()}($parameters)\n") + } + + data class EnumCase( + val name: String, + val param: TypeName?, + val initCheck: String, + val caseArg: TypeName, + val isObject: Boolean, + val explodedParams: List>, + ) { + val initBlock: String = if (isObject) { + "" + } else { + "(" + .plus( + explodedParams.joinToString(",\n") { + val tupleType = it.second as? TupleTypeName + val paramType = (it.second as? ParameterizedTypeName) + when { + tupleType != null -> tupleType.generateTuple(it.first) + it.second.isCharacter -> "${it.first}: Character(UnicodeScalar(obj.${it.first})!)" + paramType?.rawType == DICTIONARY -> paramType.toDictionaryCaster( + it.first, + ) + + paramType?.rawType == SET -> paramType.toSetCaster(it.first) + paramType?.rawType == ARRAY -> paramType.toArrayCaster(it.first) + paramType?.optional == true -> { + val unwrapped = paramType.unwrapOptional() + when ((unwrapped as? ParameterizedTypeName)?.rawType) { + DICTIONARY -> paramType.toDictionaryCaster(it.first, true) + SET -> paramType.toSetCaster(it.first, true) + ARRAY -> paramType.toArrayCaster(it.first, true) + else -> paramType.generateInitParameter(it.first) + } + } + + else -> it.second.generateInitParameter(it.first) + } + }, + ) + .plus(")") + } + + val caseBlock = if (isObject) { + "" + } else { + "(" + explodedParams.joinToString { "let ${it.first}" } + ")" + } + val enumCaseSpec: EnumerationCaseSpec + get() { + return if (param == null) { + EnumerationCaseSpec.builder(name).build() + } else if (explodedParams.isNotEmpty()) { + val stripGenericsFromObjC = explodedParams.map { param -> + (param.second as? ParameterizedTypeName)?.let { + if (it.rawType.moduleName != "Swift") { + param.first to it.rawType.parameterizedBy( + *it.typeArguments.stripInnerGenerics().toTypedArray(), + ) + } else { + null + } + } ?: param + } + EnumerationCaseSpec.builder( + name = name, + type = TupleTypeName.of(*stripGenericsFromObjC.toTypedArray()), + ).build() + } else { + EnumerationCaseSpec.builder(name, param).build() + } + } + } + + class Config : BaseConfig { + override var filter: Filter = Filter.Exclude(emptySet()) + } + + companion object : Factory { + override fun create(block: Config.() -> Unit): SealedToSwiftAssociatedEnumFeature { + val config = Config().apply(block) + return SealedToSwiftAssociatedEnumFeature(featureContext, config.filter) + } + + override val featureContext: KClass = ClassContext::class + + @JvmStatic + override val factory = Companion + } +} + +private fun ParameterizedTypeName.toArrayCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) + .plus("]") + .plus(if (optional) " : nil" else "") + +private fun ParameterizedTypeName.toSetCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! Set<") + .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) + .plus(">") + .plus(if (optional) " : nil" else "") + +private fun ParameterizedTypeName.toDictionaryCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) + .plus(" : ") + .plus(this.typeArguments[1].getKotlinInteropTypeWithFallback()) + .plus("]") + .plus(if (optional) " : nil" else "") + +private fun TupleTypeName.generateTuple(paramName: String): String = + if (this.types.size == 2) { + "$paramName: (" + .plus( + this.types[0].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.first", + ), + ) + .plus(", ") + .plus( + this.types[1].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.second", + ), + ) + .plus(")") + } else { + "$paramName: (" + .plus( + this.types[0].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.first", + ), + ) + .plus(", ") + .plus( + this.types[1].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.second", + ), + ) + .plus(", ") + .plus( + this.types[2].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.third", + ), + ) + .plus(")") + } + +private val TypeName.firstTypeArgument: TypeName? + get() = (this as? ParameterizedTypeName)?.let { + it.typeArguments.first() + } + +private fun TypeName.getKotlinInteropTypeWithFallback(): String { + return ( + this.firstTypeArgument?.getKotlinInteropFromSwiftType() + ?: this.getKotlinInteropFromSwiftType() + ?: this.name + ) + .replace("?", "") +} + +private fun SealedToSwiftAssociatedEnumFeature.EnumCase.swiftToKotlinConstructor(): String = + explodedParams.joinToString { (paramName, paramType) -> + "$paramName: " + when { + paramType.isCharacter -> "$paramName.utf16.first!" + paramType is TupleTypeName -> { + if (paramType.types.size == 2) { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + + "KotlinPair<" + .plus(firstType.getKotlinInteropTypeWithFallback().toNSString()) + .plus(", ") + .plus(secondType.getKotlinInteropTypeWithFallback().toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(")") + } else if (paramType.types.size == 3) { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + val third = paramType.types[2] + val thirdType = third.second + "KotlinTriple<" + .plus(firstType.getKotlinInteropTypeWithFallback().toNSString()) + .plus(", ") + .plus(secondType.getKotlinInteropTypeWithFallback().toNSString()) + .plus(", ") + .plus(thirdType.getKotlinInteropTypeWithFallback().toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(", third: ") + .plus( + thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), + ) + .plus(")") + } else { + "unknown tuple type" + } + } + + else -> paramType.generateKotlinConstructorIfNecessaryForParameter(paramName) + } + } + +private fun String.toNSString(): String = + if (this == "String" || this == "Swift.String") { + "NSString" + } else { + this + } + +private fun TypeName.generateKotlinConstructorIfNecessaryForParameter(paramName: String): String { + return when { + this.optional -> this.generateKotlinConstructorIfNecessary(paramName, false) + else -> paramName + } +} + +private fun TypeName.generateKotlinConstructorIfNecessary( + paramName: String, + isForTuple: Boolean = true, +): String { + val unwrapped = this.firstTypeArgument + return when { + unwrapped != null -> unwrapped.generateKotlinConstructorForNullableType(paramName) + this.optional && !isForTuple -> this.generateKotlinConstructorForNullableType(paramName) + else -> generateKotlinConstructorForNonNullableType(paramName) + }.let { + if (!isForTuple) { + it + } else if (this == STRING) { + it.replace(paramName, "$paramName as NSString") + } else if (unwrapped == STRING) { + it.replace("? $paramName :", "? $paramName! as NSString :") + } else { + it + } + } +} + +private fun TypeName.getKotlinInteropFromSwiftType(): String? = + swiftTypeToKotlinMap[this]?.replace("kotlin/", "Kotlin") + +private val TypeName.swiftRetriver: String + get() = (if (!this.optional) "!" else "?") + .plus(".") + .plus( + this.name.split(".").last().lowercase() + .replace("?", "") + .let { + when (it) { + "float32" -> "float" + "float64" -> "double" + else -> it + } + }, + ) + .plus("Value") + +private fun TypeName.generateSwiftRetrieverForKotlinType( + paramName: String, + isForTuple: Boolean = true, +): String = + if (swiftTypeToKotlinMap.containsKey(this) || swiftOptionalTypeToKotlinMap.containsKey(this)) { + "$paramName" + .plus( + if (isForTuple || this.optional) { + this.swiftRetriver + } else { + "" + }, + ) + } else if (this == STRING) { + "$paramName${if (isForTuple) "!" else ""} as String" + } else if (this == STRING.wrapOptional()) { + "$paramName != nil ? $paramName! as String : nil" + } else { + "$paramName${if (!this.optional && isForTuple) "!" else ""}" + } + +private fun TypeName.generateKotlinConstructorForNonNullableType(paramName: String): String { + return this.getKotlinInteropFromSwiftType()?.plus("(value: $paramName)") + ?: paramName +} + +private fun TypeName.generateKotlinConstructorForNullableType(paramName: String): String { + return "$paramName != nil ? " + .plus( + this.getKotlinInteropFromSwiftType()?.plus("(value: $paramName!)") + ?: paramName, + ) + .plus(" : nil") +} + +private fun List.stripInnerGenerics(): List = map { + (it as? ParameterizedTypeName)?.let { + if (it.rawType.simpleName.contains("NS")) it.rawType else null + } ?: it +} + +private val kotlinToSwiftTypeMap: Map = mapOf( + "kotlin/Any" to ANY_OBJECT, + "kotlin/Boolean" to BOOL, + "kotlin/Byte" to INT8, + "kotlin/Double" to FLOAT64, + "kotlin/Float" to FLOAT32, + "kotlin/Int" to INT32, + "kotlin/Long" to INT64, + "kotlin/Short" to INT16, + "kotlin/UByte" to UINT8, + "kotlin/UInt" to UINT32, + "kotlin/ULong" to UINT64, + "kotlin/UShort" to UIN16, +) + +val swiftTypeToKotlinMap: Map = mapOf( + ANY_OBJECT to "kotlin/Any", + BOOL to "kotlin/Boolean", + INT8 to "kotlin/Byte", + FLOAT64 to "kotlin/Double", + FLOAT32 to "kotlin/Float", + INT32 to "kotlin/Int", + INT64 to "kotlin/Long", + INT16 to "kotlin/Short", + UINT8 to "kotlin/UByte", + UINT32 to "kotlin/UInt", + UINT64 to "kotlin/ULong", + UIN16 to "kotlin/UShort", +) + +val swiftOptionalTypeToKotlinMap: Map = + swiftTypeToKotlinMap.map { (swiftType, kotlinName) -> + swiftType.wrapOptional() to kotlinName + } + .toMap() + +private fun String.kotlinPrimitiveTypeNameToSwift( + moduleName: String, + arguments: List, +): TypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") + "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") + "kotlin/Pair" -> arguments.generateTupleType(moduleName) + "kotlin/Result" -> ANY_OBJECT + "kotlin/String" -> STRING + "kotlin/Triple" -> arguments.generateTupleType(moduleName) + "kotlin/Throwable" -> DeclaredTypeName( + moduleName = moduleName, + simpleName = "KotlinThrowable", + ) + + "kotlin/Unit" -> VOID + "kotlin/collections/List" -> ARRAY + "kotlin/collections/Map" -> DICTIONARY + "kotlin/collections/Set" -> SET + else -> { + if (this.startsWith("kotlin/Function")) { + val typedArgs = arguments.getTypes(moduleName, NamingMode.KOTLIN, false) + val types = typedArgs.map { ParameterSpec.unnamed(it) }.dropLast(1) + FunctionTypeName.get(types, typedArgs.last()) + } else { + kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) + } + } + } +} + +private fun List.generateTupleType(moduleName: String): TupleTypeName = + TupleTypeName.of( + *this + .map { projection -> + (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) + .let { + if (projection.type?.isNullable == true && !it.optional) { + it.wrapOptional() + } else { + it + } + } + } + .map { "" to it } + .toTypedArray(), + ) + +private fun KmType.kotlinPrimitiveTypeNameToSwift(moduleName: String): TypeName? { + val typeName = this.nameAsString + + return when { + typeName == null -> null + typeName.startsWith("kotlin/") -> + typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments) + + else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) + }?.addGenericsAndOptional( + kmType = this, + moduleName = moduleName, + namingMode = null, + isOuterSwift = true, + ) +} + +private val NSSTRING = DeclaredTypeName(moduleName = "Foundation", simpleName = "NSString") + +private fun String.kotlinPrimitiveTypeNameToObjectiveC(moduleName: String): DeclaredTypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/Any" -> ANY_OBJECT + "kotlin/Boolean" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinBoolean") + "kotlin/Pair" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinPair") + "kotlin/Result" -> ANY_OBJECT + "kotlin/String" -> NSSTRING + "kotlin/Short" -> DeclaredTypeName(moduleName = "Foundation", simpleName = "NSNumber") + "kotlin/Triple" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinTriple") + "kotlin/collections/Map" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSDictionary", + ) + + "kotlin/collections/Set" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSSet", + ) + + "kotlin/collections/List" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSArray", + ) + + else -> this.kotlinInteropName(moduleName) + } +} + +private fun String.kotlinInteropName(moduleName: String) = DeclaredTypeName( + moduleName = moduleName, + simpleName = "Kotlin" + this.split("/").last(), +) + +private fun getDeclaredTypeNameFromNonPrimitive( + typeName: String, + moduleName: String, +) = if (typeName.startsWith("platform/")) { + val withoutCompanion: String = typeName.removeSuffix(".Companion") + val moduleAndClass: List = withoutCompanion.split("/").drop(1) + val module: String = moduleAndClass[0] + val className: String = moduleAndClass[1] + + DeclaredTypeName.typeName( + listOf(module, className).joinToString("."), + ).objcNameToSwift() +} else { + // take type after final slash and generate declared type assuming module name + val simpleName: String = typeName.split("/").last() + DeclaredTypeName( + moduleName = moduleName, + simpleName = simpleName, + ) +} + +private fun TypeName.addGenericsAndOptional( + kmType: KmType, + moduleName: String, + namingMode: NamingMode?, + isOuterSwift: Boolean, +): TypeName { + val isSwift = (this as? DeclaredTypeName)?.moduleName == "Swift" + + return if (this is DeclaredTypeName && kmType.hasGenerics) { + val genericTypes = kmType.arguments.getTypes( + moduleName = moduleName, + namingMode = when { + this.simpleName.startsWith("Kotlin") -> NamingMode.KOTLIN_NO_STRING + this == ARRAY || this == SET || this == DICTIONARY -> NamingMode.KOTLIN + namingMode != null -> namingMode + isSwift -> NamingMode.SWIFT + else -> NamingMode.OBJC + }, + isOuterSwift = isSwift, + ) + this.parameterizedBy(*genericTypes.toTypedArray()) + } else { + this + }.let { + if (kmType.isNullable && isOuterSwift) it.makeOptional() else it + } +} + +enum class NamingMode { KOTLIN, KOTLIN_NO_STRING, SWIFT, OBJC } + +private fun List.getTypes( + moduleName: String, + namingMode: NamingMode, + isOuterSwift: Boolean, +): List = this.map { + it.type?.kotlinTypeNameToInner(moduleName, namingMode, isOuterSwift) ?: ANY_OBJECT +} + +private fun KmType.kotlinTypeNameToInner( + moduleName: String, + namingMode: NamingMode, + isOuterSwift: Boolean, +): TypeName? { + val typeName = this.nameAsString + return when { + typeName == null -> null + typeName.startsWith("kotlin/") -> { + when (namingMode) { + NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) + NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) + NamingMode.KOTLIN_NO_STRING -> + typeName + .kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + .let { if (it == STRING) NSSTRING else it } + } + } + + else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) + }?.addGenericsAndOptional( + kmType = this, + moduleName = moduleName, + namingMode = namingMode, + isOuterSwift = isOuterSwift, + ) +} + +private fun String.kotlinPrimitiveTypeNameToKotlinInterop(moduleName: String): TypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/String" -> STRING + "kotlin/collections/List" -> ARRAY + "kotlin/collections/Map" -> DICTIONARY + "kotlin/collections/Set" -> SET + else -> this.kotlinInteropName(moduleName) + } +} + +val TypeName.isCharacter: Boolean + get() = this.name == "Swift.Character" + +private val KmType.isNullable: Boolean + get() = Flag.Type.IS_NULLABLE(flags) + +private val KmType.hasGenerics: Boolean + get() = this.arguments.isNotEmpty() + +private val KmType.nameAsString: String? + get() { + val classifier = this.classifier + return when (classifier) { + is KmClassifier.Class -> classifier.name + is KmClassifier.TypeParameter -> null + is KmClassifier.TypeAlias -> classifier.name + } + } + +private fun TypeName.generateInitParameter(paramName: String): String { + return "$paramName: " + .plus( + this.generateSwiftRetrieverForKotlinType( + paramName = "obj.$paramName", + isForTuple = false, + ), + ) +} diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt new file mode 100644 index 0000000..255484a --- /dev/null +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt @@ -0,0 +1,11 @@ +package dev.icerock.moko.kswift.plugin.model + +object TestInstances { + val hasChar = HasChar('a') + val hasEnum = HasEnum(OwnEnum.A) + val hasFunction = HasFunction { i, l, s -> "$i $l $s" } + val hasNullableListNull = HasNullableOuterList(null) + val hasNullableList = HasNullableOuterList(emptyList()) + val hasInnerList = HasInnerList(listOf(listOf(true, false), listOf(true))) + val hasInnerNullable = HasInnerNullable(listOf(listOf(true, null), listOf(null))) +} diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt new file mode 100644 index 0000000..8b6fe0c --- /dev/null +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt @@ -0,0 +1,75 @@ +package dev.icerock.moko.kswift.plugin.model + +@Suppress("LongLine") +sealed interface TestingSealed +data class HasChar(val mychar: Char) : TestingSealed +data class HasEnum(val myenum: OwnEnum) : TestingSealed +data class HasFunction(val myfunc: (Int, List, String) -> String) : TestingSealed +data class HasNullableOuterList(val innerList: List>?) : TestingSealed +data class HasInnerList(val innerList: List>) : TestingSealed +data class HasNullableInnerList(val innerList: List?>) : TestingSealed +data class HasInnerNullable(val innerList: List>) : TestingSealed +data class HasListInt(val hasGeneric: List) : TestingSealed +data class HasListIntNullable(val hasGeneric: List) : TestingSealed +data class HasListString(val hasGeneric: List) : TestingSealed +data class HasListStringOuterNullable(val hasGeneric: List?) : TestingSealed +data class HasListStringNullable(val hasGeneric: List) : TestingSealed +data class HasListOwn(val hasGeneric: List) : TestingSealed +data class HasMap(val map: Map) : TestingSealed +data class HasMapNullableParams(val map: Map) : TestingSealed +data class HasMapNullableOuter(val map: Map?) : TestingSealed +data class HasMultipleOwnParams(val p1: OwnClass, val p2: OwnClass?) : TestingSealed +data class HasNestedGeneric(val nested: List>) : TestingSealed +data class HasSomeNullables( + val myint: Int, + val myintopt: Int?, + val uintnotoptional: UInt, + val uintoptional: UInt?, + val mybool: Boolean, + val optbool: Boolean?, +) : TestingSealed +data class HasOtherNullables( + val mystring: String, + val optstring: String?, + val myfloat: Float, + val optfloat: Float?, + val mydouble: Double, + val optdouble: Double?, +) : TestingSealed +data class HasOwnClass(val ownClass: OwnClass) : TestingSealed +data class HasOwnClassWithGeneric(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericAny(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericEnum(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericInnerMap(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed +data class HasOwnClassWithGenericInnerPair( + val ownClassWithGeneric: OwnHasGeneric>, +) : TestingSealed +data class HasOwnClassWithGenericInnerSet(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed +data class HasOwnClassWithGenericNested(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed +data class HasOwnClassWithGenericNullable(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericThrowable(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericWildcard(val ownClassWithGeneric: OwnHasGeneric<*>) : TestingSealed +data class HasPairGeneric(val pair: Pair) : TestingSealed +data class HasPairBool(val pair: Pair) : TestingSealed +data class HasPairString(val pair: Pair) : TestingSealed +data class HasPairFloat(val pair: Pair) : TestingSealed +data class HasSet(val myset: Set) : TestingSealed +data class HasSetNullableOuter(val myset: Set?) : TestingSealed +data class HasSetNullableInt(val myset: Set) : TestingSealed +data class HasSetString(val myset: Set) : TestingSealed +data class HasSetStringNullable(val myset: Set) : TestingSealed +data class HasThrowable(val throwable: Throwable) : TestingSealed +data class HasTriple(val triple: Triple) : TestingSealed { + val anotherProp = "anotherProp" +} +object JustAnObj : TestingSealed + +data class OwnClass(val whatever: String) + +class OwnHasGeneric { + val value: T? = null +} + +enum class OwnEnum { + A, B, C +} From ae2b0054c78572345da2d3db19bd5be3a51982e5 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 20 Feb 2023 15:09:19 -0800 Subject: [PATCH 03/16] added test --- .../SealedToSwiftAssociatedEnumFeature.kt | 25 +- .../moko/kswift/plugin/InMemoryAppendable.kt | 21 ++ .../SealedToSwiftAssociatedEnumFeatureTest.kt | 294 ++++++++++++++++++ .../moko/kswift/plugin/model/TestInstances.kt | 11 - .../src/test/resources/associated-enum.klib | Bin 0 -> 34010 bytes sample/mpp-library/build.gradle.kts | 2 +- .../library/associatedenum/TestInstances.kt | 19 ++ .../library/associatedenum}/TestingSealed.kt | 2 +- 8 files changed, 357 insertions(+), 17 deletions(-) create mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/InMemoryAppendable.kt create mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt delete mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt create mode 100644 kswift-gradle-plugin/src/test/resources/associated-enum.klib create mode 100644 sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestInstances.kt rename {kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model => sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum}/TestingSealed.kt (98%) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index c7573ca..487c488 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -15,6 +15,7 @@ import io.outfoxx.swiftpoet.DeclaredTypeName import io.outfoxx.swiftpoet.EnumerationCaseSpec import io.outfoxx.swiftpoet.FLOAT32 import io.outfoxx.swiftpoet.FLOAT64 +import io.outfoxx.swiftpoet.FileSpec import io.outfoxx.swiftpoet.FunctionSpec import io.outfoxx.swiftpoet.FunctionTypeName import io.outfoxx.swiftpoet.INT16 @@ -54,15 +55,28 @@ class SealedToSwiftAssociatedEnumFeature( @Suppress("ReturnCount") override fun doProcess(featureContext: ClassContext, processorContext: ProcessorContext) { - if (featureContext.clazz.sealedSubclasses.isEmpty()) return - val kotlinFrameworkName: String = processorContext.framework.baseName + + doProcess( + featureContext = featureContext, + fileSpecBuilder = processorContext.fileSpecBuilder, + kotlinFrameworkName = kotlinFrameworkName, + ) + } + + fun doProcess( + featureContext: ClassContext, + fileSpecBuilder: FileSpec.Builder, + kotlinFrameworkName: String, + ) { val kmClass: KmClass = featureContext.clazz if (Flag.IS_PUBLIC(kmClass.flags).not()) return val originalClassName: String = getSimpleName(kmClass.name, featureContext.kLibClasses) + if (featureContext.clazz.sealedSubclasses.isEmpty()) return + println("Generating enum for sealed class $originalClassName") val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) @@ -97,7 +111,7 @@ class SealedToSwiftAssociatedEnumFeature( ) .build() - processorContext.fileSpecBuilder.addType(enumType) + fileSpecBuilder.addType(enumType) } private fun buildEnumConstructor( @@ -282,7 +296,10 @@ class SealedToSwiftAssociatedEnumFeature( paramType?.optional == true -> { val unwrapped = paramType.unwrapOptional() when ((unwrapped as? ParameterizedTypeName)?.rawType) { - DICTIONARY -> paramType.toDictionaryCaster(it.first, true) + DICTIONARY -> { + // TODO: unwrapped for other classes? + unwrapped.toDictionaryCaster(it.first, true) + } SET -> paramType.toSetCaster(it.first, true) ARRAY -> paramType.toArrayCaster(it.first, true) else -> paramType.generateInitParameter(it.first) diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/InMemoryAppendable.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/InMemoryAppendable.kt new file mode 100644 index 0000000..1c184af --- /dev/null +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/InMemoryAppendable.kt @@ -0,0 +1,21 @@ +package dev.icerock.moko.kswift.plugin + +class InMemoryAppendable : Appendable { + private val sb = StringBuilder() + override fun append(csq: CharSequence?): java.lang.Appendable { + sb.append(csq) + return this + } + + override fun append(csq: CharSequence?, start: Int, end: Int): java.lang.Appendable { + sb.insert(0, csq, start, end) + return this + } + + override fun append(c: Char): java.lang.Appendable { + sb.append(c) + return this + } + + override fun toString(): String = sb.toString() +} diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt new file mode 100644 index 0000000..21e2d63 --- /dev/null +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -0,0 +1,294 @@ +package dev.icerock.moko.kswift.plugin + +import dev.icerock.moko.kswift.plugin.context.ClassContext +import dev.icerock.moko.kswift.plugin.context.LibraryContext +import dev.icerock.moko.kswift.plugin.feature.Filter +import dev.icerock.moko.kswift.plugin.feature.SealedToSwiftAssociatedEnumFeature +import io.outfoxx.swiftpoet.FileSpec +import kotlinx.metadata.klib.KlibModuleMetadata +import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy +import org.jetbrains.kotlin.library.resolveSingleFileKlib +import kotlin.test.Test +import kotlin.test.assertEquals + +class SealedToSwiftAssociatedEnumFeatureTest { + @Test + fun `associated enum feature should produce type mapped output`() { + // Generated from TestSealed.kt + val klibPath = this::class.java.classLoader.getResource("associated-enum.klib") + val konanFile = org.jetbrains.kotlin.konan.file.File(klibPath.toURI().path) + // Need to use tooling strategy here since the klib was generated with 1.8 + val library = resolveSingleFileKlib(konanFile, strategy = ToolingSingleFileKlibResolveStrategy) + val metadata = KlibModuleMetadata.read(KotlinMetadataLibraryProvider(library)) + val libraryContext = LibraryContext(metadata) + val fileSpecBuilder = FileSpec.builder(moduleName = "module", fileName = "file") + + libraryContext.visit { featureContext -> + (featureContext as? ClassContext)?.let { + SealedToSwiftAssociatedEnumFeature( + featureContext = ClassContext::class, + filter = Filter.Exclude(emptySet()), + ).doProcess( + featureContext = it, + fileSpecBuilder = fileSpecBuilder, + kotlinFrameworkName = "shared" + ) + } + } + + val appendable = InMemoryAppendable() + fileSpecBuilder.build().writeTo(appendable) + val expected = """import Foundation +import shared + +/** + * selector: ClassContext/associatedenum/com/icerockdev/library/associatedenum/TestingSealed */ +public enum TestingSealedKs { + + case hasChar(mychar: Character) + case hasEnum(myenum: OwnEnum) + case hasFunction(myfunc: ( + KotlinInt, + [KotlinBoolean], + String + ) -> String) + case hasInnerList(innerList: [[KotlinBoolean]]) + case hasInnerNullable(innerList: [[KotlinBoolean?]]) + case hasListInt(hasGeneric: [KotlinInt]) + case hasListIntNullable(hasGeneric: [KotlinInt?]) + case hasListOwn(hasGeneric: [OwnClass]) + case hasListString(hasGeneric: [String]) + case hasListStringNullable(hasGeneric: [String?]) + case hasListStringOuterNullable(hasGeneric: [String]?) + case hasMap(map: [String : KotlinInt]) + case hasMapNullableOuter(map: [String : KotlinInt]?) + case hasMapNullableParams(map: [String : KotlinInt?]) + case hasMultipleOwnParams(p1: OwnClass, p2: OwnClass?) + case hasNestedGeneric(nested: [KotlinPair]) + case hasNullableInnerList(innerList: [[KotlinBoolean]?]) + case hasNullableOuterList(innerList: [[KotlinBoolean]]?) + case hasOtherNullables(mystring: String, optstring: String?, myfloat: Float32, optfloat: Float32?, mydouble: Float64, optdouble: Float64?) + case hasOwnClass(ownClass: OwnClass) + case hasOwnClassWithGeneric(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericAny(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericEnum(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericInnerMap(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericInnerPair(ownClassWithGeneric: OwnHasGeneric>) + case hasOwnClassWithGenericInnerSet(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericNested(ownClassWithGeneric: OwnHasGeneric>) + case hasOwnClassWithGenericNullable(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericThrowable(ownClassWithGeneric: OwnHasGeneric) + case hasOwnClassWithGenericWildcard(ownClassWithGeneric: OwnHasGeneric) + case hasPairBool(pair: (Bool, Bool?)) + case hasPairFloat(pair: (Float32, Float32?)) + case hasPairGeneric(pair: (UInt8, OwnClass?)) + case hasPairString(pair: (String, String?)) + case hasSet(myset: Set) + case hasSetNullableInt(myset: Set) + case hasSetNullableOuter(myset: Set?) + case hasSetString(myset: Set) + case hasSetStringNullable(myset: Set) + case hasSomeNullables(myint: Int32, myintopt: Int32?, uintnotoptional: UInt32, uintoptional: UInt32?, mybool: Bool, optbool: Bool?) + case hasThrowable(throwable: KotlinThrowable) + case hasTriple(triple: (Float32, Int32?, OwnClass)) + case justAnObj + + public var sealed: TestingSealed { + switch self { + case .hasChar(let mychar): + return HasChar(mychar: mychar.utf16.first!) + case .hasEnum(let myenum): + return HasEnum(myenum: myenum) + case .hasFunction(let myfunc): + return HasFunction(myfunc: myfunc) + case .hasInnerList(let innerList): + return HasInnerList(innerList: innerList) + case .hasInnerNullable(let innerList): + return HasInnerNullable(innerList: innerList) + case .hasListInt(let hasGeneric): + return HasListInt(hasGeneric: hasGeneric) + case .hasListIntNullable(let hasGeneric): + return HasListIntNullable(hasGeneric: hasGeneric) + case .hasListOwn(let hasGeneric): + return HasListOwn(hasGeneric: hasGeneric) + case .hasListString(let hasGeneric): + return HasListString(hasGeneric: hasGeneric) + case .hasListStringNullable(let hasGeneric): + return HasListStringNullable(hasGeneric: hasGeneric) + case .hasListStringOuterNullable(let hasGeneric): + return HasListStringOuterNullable(hasGeneric: hasGeneric != nil ? hasGeneric : nil) + case .hasMap(let map): + return HasMap(map: map) + case .hasMapNullableOuter(let map): + return HasMapNullableOuter(map: map != nil ? map : nil) + case .hasMapNullableParams(let map): + return HasMapNullableParams(map: map) + case .hasMultipleOwnParams(let p1, let p2): + return HasMultipleOwnParams(p1: p1, p2: p2 != nil ? p2 : nil) + case .hasNestedGeneric(let nested): + return HasNestedGeneric(nested: nested) + case .hasNullableInnerList(let innerList): + return HasNullableInnerList(innerList: innerList) + case .hasNullableOuterList(let innerList): + return HasNullableOuterList(innerList: innerList != nil ? innerList : nil) + case .hasOtherNullables(let mystring, let optstring, let myfloat, let optfloat, let mydouble, let optdouble): + return HasOtherNullables(mystring: mystring, optstring: optstring != nil ? optstring : nil, myfloat: myfloat, optfloat: optfloat != nil ? KotlinFloat(value: optfloat!) : nil, mydouble: mydouble, optdouble: optdouble != nil ? KotlinDouble(value: optdouble!) : nil) + case .hasOwnClass(let ownClass): + return HasOwnClass(ownClass: ownClass) + case .hasOwnClassWithGeneric(let ownClassWithGeneric): + return HasOwnClassWithGeneric(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericAny(let ownClassWithGeneric): + return HasOwnClassWithGenericAny(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericEnum(let ownClassWithGeneric): + return HasOwnClassWithGenericEnum(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericInnerMap(let ownClassWithGeneric): + return HasOwnClassWithGenericInnerMap(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericInnerPair(let ownClassWithGeneric): + return HasOwnClassWithGenericInnerPair(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericInnerSet(let ownClassWithGeneric): + return HasOwnClassWithGenericInnerSet(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericNested(let ownClassWithGeneric): + return HasOwnClassWithGenericNested(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericNullable(let ownClassWithGeneric): + return HasOwnClassWithGenericNullable(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericThrowable(let ownClassWithGeneric): + return HasOwnClassWithGenericThrowable(ownClassWithGeneric: ownClassWithGeneric) + case .hasOwnClassWithGenericWildcard(let ownClassWithGeneric): + return HasOwnClassWithGenericWildcard(ownClassWithGeneric: ownClassWithGeneric) + case .hasPairBool(let pair): + return HasPairBool(pair: KotlinPair(first: KotlinBoolean(value: pair.0), second: pair.1 != nil ? KotlinBoolean(value: pair.1!) : nil)) + case .hasPairFloat(let pair): + return HasPairFloat(pair: KotlinPair(first: KotlinFloat(value: pair.0), second: pair.1 != nil ? KotlinFloat(value: pair.1!) : nil)) + case .hasPairGeneric(let pair): + return HasPairGeneric(pair: KotlinPair(first: KotlinUByte(value: pair.0), second: pair.1 != nil ? pair.1 : nil)) + case .hasPairString(let pair): + return HasPairString(pair: KotlinPair(first: pair.0 as NSString, second: pair.1 != nil ? pair.1! as NSString : nil)) + case .hasSet(let myset): + return HasSet(myset: myset) + case .hasSetNullableInt(let myset): + return HasSetNullableInt(myset: myset) + case .hasSetNullableOuter(let myset): + return HasSetNullableOuter(myset: myset != nil ? myset : nil) + case .hasSetString(let myset): + return HasSetString(myset: myset) + case .hasSetStringNullable(let myset): + return HasSetStringNullable(myset: myset) + case .hasSomeNullables(let myint, let myintopt, let uintnotoptional, let uintoptional, let mybool, let optbool): + return HasSomeNullables(myint: myint, myintopt: myintopt != nil ? KotlinInt(value: myintopt!) : nil, uintnotoptional: uintnotoptional, uintoptional: uintoptional != nil ? KotlinUInt(value: uintoptional!) : nil, mybool: mybool, optbool: optbool != nil ? KotlinBoolean(value: optbool!) : nil) + case .hasThrowable(let throwable): + return HasThrowable(throwable: throwable) + case .hasTriple(let triple): + return HasTriple(triple: KotlinTriple(first: KotlinFloat(value: triple.0), second: triple.1 != nil ? KotlinInt(value: triple.1!) : nil, third: triple.2)) + case .justAnObj: + return JustAnObj() + } + } + + public init(_ obj: TestingSealed) { + if let obj = obj as? shared.HasChar { + self = .hasChar(mychar: Character(UnicodeScalar(obj.mychar)!)) + } else if let obj = obj as? shared.HasEnum { + self = .hasEnum(myenum: obj.myenum) + } else if let obj = obj as? shared.HasFunction { + self = .hasFunction(myfunc: obj.myfunc) + } else if let obj = obj as? shared.HasInnerList { + self = .hasInnerList(innerList: obj.innerList as! [[shared.KotlinBoolean]]) + } else if let obj = obj as? shared.HasInnerNullable { + self = .hasInnerNullable(innerList: obj.innerList as! [[shared.KotlinBoolean]]) + } else if let obj = obj as? shared.HasListInt { + self = .hasListInt(hasGeneric: obj.hasGeneric as! [shared.KotlinInt]) + } else if let obj = obj as? shared.HasListIntNullable { + self = .hasListIntNullable(hasGeneric: obj.hasGeneric as! [shared.KotlinInt]) + } else if let obj = obj as? shared.HasListOwn { + self = .hasListOwn(hasGeneric: obj.hasGeneric as! [shared.OwnClass]) + } else if let obj = obj as? shared.HasListString { + self = .hasListString(hasGeneric: obj.hasGeneric as! [Swift.String]) + } else if let obj = obj as? shared.HasListStringNullable { + self = .hasListStringNullable(hasGeneric: obj.hasGeneric as! [Swift.String]) + } else if let obj = obj as? shared.HasListStringOuterNullable { + self = .hasListStringOuterNullable(hasGeneric: obj.hasGeneric != nil ? obj.hasGeneric as! [[Swift.String]] : nil) + } else if let obj = obj as? shared.HasMap { + self = .hasMap(map: obj.map as! [Swift.String : shared.KotlinInt]) + } else if let obj = obj as? shared.HasMapNullableOuter { + self = .hasMapNullableOuter(map: obj.map != nil ? obj.map as! [Swift.String : shared.KotlinInt] : nil) + } else if let obj = obj as? shared.HasMapNullableParams { + self = .hasMapNullableParams(map: obj.map as! [Swift.String : shared.KotlinInt]) + } else if let obj = obj as? shared.HasMultipleOwnParams { + self = .hasMultipleOwnParams(p1: obj.p1, + p2: obj.p2) + } else if let obj = obj as? shared.HasNestedGeneric { + self = .hasNestedGeneric(nested: obj.nested as! [shared.KotlinPair]) + } else if let obj = obj as? shared.HasNullableInnerList { + self = .hasNullableInnerList(innerList: obj.innerList as! [[shared.KotlinBoolean]]) + } else if let obj = obj as? shared.HasNullableOuterList { + self = .hasNullableOuterList(innerList: obj.innerList != nil ? obj.innerList as! [[[shared.KotlinBoolean]]] : nil) + } else if let obj = obj as? shared.HasOtherNullables { + self = .hasOtherNullables(mystring: obj.mystring as String, + optstring: obj.optstring != nil ? obj.optstring! as String : nil, + myfloat: obj.myfloat, + optfloat: obj.optfloat?.floatValue, + mydouble: obj.mydouble, + optdouble: obj.optdouble?.doubleValue) + } else if let obj = obj as? shared.HasOwnClass { + self = .hasOwnClass(ownClass: obj.ownClass) + } else if let obj = obj as? shared.HasOwnClassWithGeneric { + self = .hasOwnClassWithGeneric(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericAny { + self = .hasOwnClassWithGenericAny(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericEnum { + self = .hasOwnClassWithGenericEnum(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericInnerMap { + self = .hasOwnClassWithGenericInnerMap(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericInnerPair { + self = .hasOwnClassWithGenericInnerPair(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericInnerSet { + self = .hasOwnClassWithGenericInnerSet(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericNested { + self = .hasOwnClassWithGenericNested(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericNullable { + self = .hasOwnClassWithGenericNullable(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericThrowable { + self = .hasOwnClassWithGenericThrowable(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasOwnClassWithGenericWildcard { + self = .hasOwnClassWithGenericWildcard(ownClassWithGeneric: obj.ownClassWithGeneric) + } else if let obj = obj as? shared.HasPairBool { + self = .hasPairBool(pair: (obj.pair.first!.boolValue, obj.pair.second?.boolValue)) + } else if let obj = obj as? shared.HasPairFloat { + self = .hasPairFloat(pair: (obj.pair.first!.floatValue, obj.pair.second?.floatValue)) + } else if let obj = obj as? shared.HasPairGeneric { + self = .hasPairGeneric(pair: (obj.pair.first!.uint8Value, obj.pair.second)) + } else if let obj = obj as? shared.HasPairString { + self = .hasPairString(pair: (obj.pair.first! as String, obj.pair.second != nil ? obj.pair.second! as String : nil)) + } else if let obj = obj as? shared.HasSet { + self = .hasSet(myset: obj.myset as! Set) + } else if let obj = obj as? shared.HasSetNullableInt { + self = .hasSetNullableInt(myset: obj.myset as! Set) + } else if let obj = obj as? shared.HasSetNullableOuter { + self = .hasSetNullableOuter(myset: obj.myset != nil ? obj.myset as! Set> : nil) + } else if let obj = obj as? shared.HasSetString { + self = .hasSetString(myset: obj.myset as! Set) + } else if let obj = obj as? shared.HasSetStringNullable { + self = .hasSetStringNullable(myset: obj.myset as! Set) + } else if let obj = obj as? shared.HasSomeNullables { + self = .hasSomeNullables(myint: obj.myint, + myintopt: obj.myintopt?.int32Value, + uintnotoptional: obj.uintnotoptional, + uintoptional: obj.uintoptional?.uint32Value, + mybool: obj.mybool, + optbool: obj.optbool?.boolValue) + } else if let obj = obj as? shared.HasThrowable { + self = .hasThrowable(throwable: obj.throwable) + } else if let obj = obj as? shared.HasTriple { + self = .hasTriple(triple: (obj.triple.first!.floatValue, obj.triple.second?.int32Value, obj.triple.third!)) + } else if obj is shared.JustAnObj { + self = .justAnObj + } else { + fatalError("TestingSealedKs not synchronized with TestingSealed class") + } + } + +} +""" + assertEquals(expected, appendable.toString()) + } +} diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt deleted file mode 100644 index 255484a..0000000 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestInstances.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.icerock.moko.kswift.plugin.model - -object TestInstances { - val hasChar = HasChar('a') - val hasEnum = HasEnum(OwnEnum.A) - val hasFunction = HasFunction { i, l, s -> "$i $l $s" } - val hasNullableListNull = HasNullableOuterList(null) - val hasNullableList = HasNullableOuterList(emptyList()) - val hasInnerList = HasInnerList(listOf(listOf(true, false), listOf(true))) - val hasInnerNullable = HasInnerNullable(listOf(listOf(true, null), listOf(null))) -} diff --git a/kswift-gradle-plugin/src/test/resources/associated-enum.klib b/kswift-gradle-plugin/src/test/resources/associated-enum.klib new file mode 100644 index 0000000000000000000000000000000000000000..cc4c87265db0710f3affb325c83a9b283ac45495 GIT binary patch literal 34010 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

YRIKo>=B5BfqwFK4<(9Zb~``B@tv^$4+AF4`$D7 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� zN98H7umf|t>UxAboP<Q(IP0_@cMc3~ZR0)QC0aZVZ1jpq)8}M4lw21+|dV zh^$`75;kf6N((OI45whJOOvAkmq(GTZ5j)Jp<3c0rO$xtB*vWgXr?j9#$C|*Dmb8XlGGpOrA zNPQ1+a|X)AK`ty!&n&;gp+0yNDAG9FYAw>LZI?0$B0h;0-b>~j1bF34$ql(T$G?+d zjwX??xXzzZ9@i#pfu1d5VV4pJBKB=bz|t7HoB2GPLODb=*pES6(B=7-6%);ENg?#7 zfl{;<0fOP-he60gPr{4oK814gG!^(9%2}RICB#YZ-+zy9{Jz2AeLAsOBtwVDJaCK# zwwld!wsaEg{HV(nZKRwX=8FZ1ENZ$}ztB9w7$d8~Zuy+%Xz+87t~w z4%n2~Hqd!KpIuNtxBE3HyVHF_i?0?Idt0xEiWT83J@h(mlr^dpS8&ZeS9pkpp?iDwk{6VeUFm9#zsbZn}#PZy}dy%R!2g>g5+PQ9dG zrD@K2gki`J5mp|cvvnF_bTAX2z7BfA)5*JX`yqV!8==fwNFne$o)ttjlg4p~!LKL9 zD~WK&t1ZRrB;I>vuTuI(a?Y$m{yZ&9dwBkX14!Y6gO?S@+)Lw3t3%BSSrJE8KP6r} z^sCa99KDrt&1h%QTBRVv%pN`#)V4Wo8}g#p<}pv&ev#fJdg4D*J2#WKzayTVPBVwE zUUC3Q3ETcONHR5R(ln$|V-eIkEG}bl>!lh_#LbO^XtDlb=FXoQa#Ic;#*E-8=rTCb z-bzVWX@jDBmE9O3XFuC-FPQ~sRD7>oG!ix)-l!0ei+I`*VELgZwc|nkI$EL(a}7VE zro-D_2I1~S!S0E6N-GM8V)bM&v(&FU?wpc zpFS<5kPVOD@UDB>8J@o?3K$uDnR~Dsuf1d4<_;LS-9Rc;+;gzA5J7g(u$-Ea7I z*H#=jnBQD%E=M3vJlnJmkKXgC! ziG1mWwBX~j>l^AatLitJOFsW_dSc_H*IyF6<^UPoJ6hK2FbtG^GH#|+^<6!n;9IF0 zr#*~4>s7g>z`d3b8-~KT4UEIPS}|(h6xlT4<*Au*7=_t>y;f}WAg6Cvwng1@YtfxlYt zQuY6f+WGJ3BmIAX2b%n|NdGVQ2`ao-ZhBM&ID~UKOLE9A)PFi6;*azj3iE~fsAza7 zzrK0EK|y->%FmzUwK4w~uYF(T_m`7gxm*+p^v}!x9JBpDz|dZSar*_#<#F5p10d5C zz=ppB{MXp+zl}?b?CJlzzJCh(ukqXe11`%Ic}@Nj_tF^d{{b2Q3UbLMQV%{rQ|Nf!H z-{4(-$ann;Ughm8c)xmO`!|@EANE`|-AmIen3oZ8@xGW{f%>;+MZbZ& z+~}ja0@sCn$obDeMtb7PqnJxzzatT{D_~wWe}Y}^6kYom9Uh$rmvAnNbGbi8eFdlES2+Ld*j+;Vom8Ax(5!5KA=R%l4>IFi zg1;>6Za1>3(=!^A?C S9Vm3jf3C "$i $l $s" } + val hasNullableListNull = HasNullableOuterList(null) + val hasNullableList = HasNullableOuterList(emptyList()) + val hasInnerList = HasInnerList(listOf(listOf(true, false), listOf(true))) + val hasInnerNullable = HasInnerNullable(listOf(listOf(true, null), listOf(null))) +} diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt similarity index 98% rename from kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt rename to sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt index 8b6fe0c..5937aa1 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/model/TestingSealed.kt +++ b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt @@ -1,4 +1,4 @@ -package dev.icerock.moko.kswift.plugin.model +package com.icerockdev.library.associatedenum @Suppress("LongLine") sealed interface TestingSealed From 1380b017fe505cfb2e13b2e7243b265d4dd7eed4 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Tue, 21 Feb 2023 09:11:21 -0800 Subject: [PATCH 04/16] decompose feature --- .../icerock/moko/kswift/plugin/KmTypeExt.kt | 7 + .../SealedToSwiftAssociatedEnumFeature.kt | 821 +----------------- .../feature/SealedToSwiftEnumFeature.kt | 46 +- .../associatedenum/AssociatedEnumCase.kt | 162 ++++ .../AssociatedEnumCaseBuilder.kt | 64 ++ .../associatedenum/KmTypeProjectionExt.kt | 32 + .../feature/associatedenum/NamingMode.kt | 3 + .../plugin/feature/associatedenum/OtherExt.kt | 170 ++++ .../ParameterizedTypeNameExt.kt | 55 ++ .../associatedenum/SwiftTypeMappings.kt | 52 ++ .../associatedenum/TupleTypeNameExt.kt | 45 + .../feature/associatedenum/TypeNameExt.kt | 144 +++ .../feature/associatedenum/TypeSpecBuilder.kt | 143 +++ 13 files changed, 912 insertions(+), 832 deletions(-) create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/NamingMode.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TupleTypeNameExt.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt create mode 100644 kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt index 9a5f569..cf2b463 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt @@ -16,6 +16,7 @@ import io.outfoxx.swiftpoet.UINT64 import io.outfoxx.swiftpoet.VOID import io.outfoxx.swiftpoet.parameterizedBy import kotlinx.metadata.ClassName +import kotlinx.metadata.Flag import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmType @@ -142,3 +143,9 @@ fun DeclaredTypeName.objcNameToSwift(): DeclaredTypeName { else -> this } } + +val KmType.isNullable: Boolean + get() = Flag.Type.IS_NULLABLE(flags) + +val KmType.hasGenerics: Boolean + get() = this.arguments.isNotEmpty() diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index 487c488..1d5c47b 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -3,49 +3,15 @@ package dev.icerock.moko.kswift.plugin.feature import dev.icerock.moko.kswift.plugin.buildTypeVariableNames import dev.icerock.moko.kswift.plugin.context.ClassContext import dev.icerock.moko.kswift.plugin.context.kLibClasses -import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics +import dev.icerock.moko.kswift.plugin.feature.associatedenum.AssociatedEnumCase +import dev.icerock.moko.kswift.plugin.feature.associatedenum.buildEnumCases +import dev.icerock.moko.kswift.plugin.feature.associatedenum.buildTypeSpec import dev.icerock.moko.kswift.plugin.getSimpleName -import dev.icerock.moko.kswift.plugin.objcNameToSwift -import io.outfoxx.swiftpoet.ANY_OBJECT -import io.outfoxx.swiftpoet.ARRAY -import io.outfoxx.swiftpoet.BOOL -import io.outfoxx.swiftpoet.CodeBlock -import io.outfoxx.swiftpoet.DICTIONARY -import io.outfoxx.swiftpoet.DeclaredTypeName -import io.outfoxx.swiftpoet.EnumerationCaseSpec -import io.outfoxx.swiftpoet.FLOAT32 -import io.outfoxx.swiftpoet.FLOAT64 import io.outfoxx.swiftpoet.FileSpec -import io.outfoxx.swiftpoet.FunctionSpec -import io.outfoxx.swiftpoet.FunctionTypeName -import io.outfoxx.swiftpoet.INT16 -import io.outfoxx.swiftpoet.INT32 -import io.outfoxx.swiftpoet.INT64 -import io.outfoxx.swiftpoet.INT8 -import io.outfoxx.swiftpoet.Modifier -import io.outfoxx.swiftpoet.ParameterSpec -import io.outfoxx.swiftpoet.ParameterizedTypeName -import io.outfoxx.swiftpoet.PropertySpec -import io.outfoxx.swiftpoet.SET -import io.outfoxx.swiftpoet.STRING -import io.outfoxx.swiftpoet.TupleTypeName -import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.TypeSpec import io.outfoxx.swiftpoet.TypeVariableName -import io.outfoxx.swiftpoet.UIN16 -import io.outfoxx.swiftpoet.UINT32 -import io.outfoxx.swiftpoet.UINT64 -import io.outfoxx.swiftpoet.UINT8 -import io.outfoxx.swiftpoet.VOID -import io.outfoxx.swiftpoet.parameterizedBy -import kotlinx.metadata.ClassName import kotlinx.metadata.Flag import kotlinx.metadata.KmClass -import kotlinx.metadata.KmClassifier -import kotlinx.metadata.KmType -import kotlinx.metadata.KmTypeProjection -import org.gradle.configurationcache.extensions.capitalized -import java.util.Locale import kotlin.reflect.KClass class SealedToSwiftAssociatedEnumFeature( @@ -79,269 +45,23 @@ class SealedToSwiftAssociatedEnumFeature( println("Generating enum for sealed class $originalClassName") - val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) + val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) if (sealedCases.isEmpty()) return val typeVariables: List = kmClass.buildTypeVariableNames(kotlinFrameworkName) val className: String = originalClassName.replace(".", "").plus("Ks") - val enumType: TypeSpec = TypeSpec.enumBuilder(className) - .addDoc("selector: ${featureContext.prefixedUniqueId}") - .apply { - typeVariables.forEach { addTypeVariable(it) } - sealedCases.forEach { addEnumCase(it.enumCaseSpec) } - } - .addModifiers(Modifier.PUBLIC) - .addFunction( - buildEnumConstructor( - featureContext = featureContext, - kotlinFrameworkName = kotlinFrameworkName, - sealedCases = sealedCases, - className = className, - originalClassName = originalClassName, - ), - ) - .addProperty( - buildSealedProperty( - featureContext = featureContext, - kotlinFrameworkName = kotlinFrameworkName, - sealedCases = sealedCases, - ), - ) - .build() - - fileSpecBuilder.addType(enumType) - } - - private fun buildEnumConstructor( - featureContext: ClassContext, - kotlinFrameworkName: String, - sealedCases: List, - className: String, - originalClassName: String, - ): FunctionSpec { - return FunctionSpec.builder("init") - .addModifiers(Modifier.PUBLIC) - .addParameter( - label = "_", - name = "obj", - type = featureContext.clazz.getDeclaredTypeNameWithGenerics( - kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses, - ), - ) - .addCode( - CodeBlock.builder() - .apply { - sealedCases.forEachIndexed { index, enumCase -> - buildString { - if (index != 0) append("} else ") - append("if ") - append(enumCase.initCheck) - append(" {") - append('\n') - }.also { add(it) } - indent() - buildString { - append("self = .") - append(enumCase.name) - append(enumCase.initBlock) - append('\n') - }.also { add(it) } - unindent() - } - add("} else {\n") - indent() - add( - "fatalError(\"$className not synchronized with $originalClassName class\")\n", - ) - unindent() - add("}\n") - } - .build(), - ) - .build() - } - - private fun buildEnumCases( - kotlinFrameworkName: String, - featureContext: ClassContext, - ): List { - val kmClass = featureContext.clazz - return kmClass.sealedSubclasses.mapNotNull { sealedClassName -> - val sealedClass: KmClass = featureContext.parentContext - .fragment.classes.first { it.name == sealedClassName } - - if (Flag.IS_PUBLIC(sealedClass.flags).not()) return@mapNotNull null - - buildEnumCase( - kotlinFrameworkName = kotlinFrameworkName, - featureContext = featureContext, - subclassName = sealedClassName, - sealedCaseClass = sealedClass, - ) - } - } - - private fun buildEnumCase( - kotlinFrameworkName: String, - featureContext: ClassContext, - subclassName: ClassName, - sealedCaseClass: KmClass, - ): EnumCase { - val kmClass = featureContext.clazz - val name: String = if (subclassName.startsWith(kmClass.name)) { - subclassName.removePrefix(kmClass.name).removePrefix(".") - } else { - subclassName.removePrefix(kmClass.name.substringBeforeLast("/")).removePrefix("/") - } - val decapitalizedName: String = name.decapitalize(Locale.ROOT) - - val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) - val caseArg = sealedCaseClass.getDeclaredTypeNameWithGenerics( - kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses, - ) - - return EnumCase( - name = decapitalizedName, - param = if (isObject) null else caseArg, - initCheck = if (isObject) { - "obj is $caseArg" - } else { - "let obj = obj as? $caseArg" - }, - caseArg = caseArg, - isObject = isObject, - explodedParams = sealedCaseClass.constructors.first().valueParameters.map { - Pair( - it.name, - it.type?.kotlinPrimitiveTypeNameToSwift(kotlinFrameworkName) - ?: DeclaredTypeName.typeName("Swift.FailedToGetReturnType"), - ) - }, - ) - } - - private fun buildSealedProperty( - featureContext: ClassContext, - kotlinFrameworkName: String, - sealedCases: List, - ): PropertySpec { - val returnType: TypeName = featureContext.clazz.getDeclaredTypeNameWithGenerics( + val enumType: TypeSpec = buildTypeSpec( + className = className, + featureContext = featureContext, + typeVariables = typeVariables, + sealedCases = sealedCases, kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses, + originalClassName = originalClassName, ) - return PropertySpec.builder("sealed", type = returnType) - .addModifiers(Modifier.PUBLIC) - .getter( - FunctionSpec - .getterBuilder() - .addCode(buildSealedPropertyBody(sealedCases)) - .build(), - ).build() - } - - private fun buildSealedPropertyBody( - sealedCases: List, - ): CodeBlock = CodeBlock.builder().apply { - add("switch self {\n") - sealedCases.forEach { enumCase -> - buildString { - append("case .") - append(enumCase.name) - append(enumCase.caseBlock) - append(":\n") - }.also { add(it) } - indent() - addSealedCaseReturnCode(enumCase) - unindent() - } - add("}\n") - }.build() - - private fun CodeBlock.Builder.addSealedCaseReturnCode( - enumCase: EnumCase, - ) { - val parameters = enumCase.swiftToKotlinConstructor() - add("return ${enumCase.name.capitalized()}($parameters)\n") - } - data class EnumCase( - val name: String, - val param: TypeName?, - val initCheck: String, - val caseArg: TypeName, - val isObject: Boolean, - val explodedParams: List>, - ) { - val initBlock: String = if (isObject) { - "" - } else { - "(" - .plus( - explodedParams.joinToString(",\n") { - val tupleType = it.second as? TupleTypeName - val paramType = (it.second as? ParameterizedTypeName) - when { - tupleType != null -> tupleType.generateTuple(it.first) - it.second.isCharacter -> "${it.first}: Character(UnicodeScalar(obj.${it.first})!)" - paramType?.rawType == DICTIONARY -> paramType.toDictionaryCaster( - it.first, - ) - - paramType?.rawType == SET -> paramType.toSetCaster(it.first) - paramType?.rawType == ARRAY -> paramType.toArrayCaster(it.first) - paramType?.optional == true -> { - val unwrapped = paramType.unwrapOptional() - when ((unwrapped as? ParameterizedTypeName)?.rawType) { - DICTIONARY -> { - // TODO: unwrapped for other classes? - unwrapped.toDictionaryCaster(it.first, true) - } - SET -> paramType.toSetCaster(it.first, true) - ARRAY -> paramType.toArrayCaster(it.first, true) - else -> paramType.generateInitParameter(it.first) - } - } - - else -> it.second.generateInitParameter(it.first) - } - }, - ) - .plus(")") - } - - val caseBlock = if (isObject) { - "" - } else { - "(" + explodedParams.joinToString { "let ${it.first}" } + ")" - } - val enumCaseSpec: EnumerationCaseSpec - get() { - return if (param == null) { - EnumerationCaseSpec.builder(name).build() - } else if (explodedParams.isNotEmpty()) { - val stripGenericsFromObjC = explodedParams.map { param -> - (param.second as? ParameterizedTypeName)?.let { - if (it.rawType.moduleName != "Swift") { - param.first to it.rawType.parameterizedBy( - *it.typeArguments.stripInnerGenerics().toTypedArray(), - ) - } else { - null - } - } ?: param - } - EnumerationCaseSpec.builder( - name = name, - type = TupleTypeName.of(*stripGenericsFromObjC.toTypedArray()), - ).build() - } else { - EnumerationCaseSpec.builder(name, param).build() - } - } + fileSpecBuilder.addType(enumType) } class Config : BaseConfig { @@ -360,522 +80,3 @@ class SealedToSwiftAssociatedEnumFeature( override val factory = Companion } } - -private fun ParameterizedTypeName.toArrayCaster( - paramName: String, - optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! [") - .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) - .plus("]") - .plus(if (optional) " : nil" else "") - -private fun ParameterizedTypeName.toSetCaster( - paramName: String, - optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! Set<") - .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) - .plus(">") - .plus(if (optional) " : nil" else "") - -private fun ParameterizedTypeName.toDictionaryCaster( - paramName: String, - optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! [") - .plus(this.typeArguments[0].getKotlinInteropTypeWithFallback()) - .plus(" : ") - .plus(this.typeArguments[1].getKotlinInteropTypeWithFallback()) - .plus("]") - .plus(if (optional) " : nil" else "") - -private fun TupleTypeName.generateTuple(paramName: String): String = - if (this.types.size == 2) { - "$paramName: (" - .plus( - this.types[0].second - .generateSwiftRetrieverForKotlinType( - "obj.$paramName.first", - ), - ) - .plus(", ") - .plus( - this.types[1].second - .generateSwiftRetrieverForKotlinType( - "obj.$paramName.second", - ), - ) - .plus(")") - } else { - "$paramName: (" - .plus( - this.types[0].second - .generateSwiftRetrieverForKotlinType( - "obj.$paramName.first", - ), - ) - .plus(", ") - .plus( - this.types[1].second - .generateSwiftRetrieverForKotlinType( - "obj.$paramName.second", - ), - ) - .plus(", ") - .plus( - this.types[2].second - .generateSwiftRetrieverForKotlinType( - "obj.$paramName.third", - ), - ) - .plus(")") - } - -private val TypeName.firstTypeArgument: TypeName? - get() = (this as? ParameterizedTypeName)?.let { - it.typeArguments.first() - } - -private fun TypeName.getKotlinInteropTypeWithFallback(): String { - return ( - this.firstTypeArgument?.getKotlinInteropFromSwiftType() - ?: this.getKotlinInteropFromSwiftType() - ?: this.name - ) - .replace("?", "") -} - -private fun SealedToSwiftAssociatedEnumFeature.EnumCase.swiftToKotlinConstructor(): String = - explodedParams.joinToString { (paramName, paramType) -> - "$paramName: " + when { - paramType.isCharacter -> "$paramName.utf16.first!" - paramType is TupleTypeName -> { - if (paramType.types.size == 2) { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second - - "KotlinPair<" - .plus(firstType.getKotlinInteropTypeWithFallback().toNSString()) - .plus(", ") - .plus(secondType.getKotlinInteropTypeWithFallback().toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(")") - } else if (paramType.types.size == 3) { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second - val third = paramType.types[2] - val thirdType = third.second - "KotlinTriple<" - .plus(firstType.getKotlinInteropTypeWithFallback().toNSString()) - .plus(", ") - .plus(secondType.getKotlinInteropTypeWithFallback().toNSString()) - .plus(", ") - .plus(thirdType.getKotlinInteropTypeWithFallback().toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(", third: ") - .plus( - thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), - ) - .plus(")") - } else { - "unknown tuple type" - } - } - - else -> paramType.generateKotlinConstructorIfNecessaryForParameter(paramName) - } - } - -private fun String.toNSString(): String = - if (this == "String" || this == "Swift.String") { - "NSString" - } else { - this - } - -private fun TypeName.generateKotlinConstructorIfNecessaryForParameter(paramName: String): String { - return when { - this.optional -> this.generateKotlinConstructorIfNecessary(paramName, false) - else -> paramName - } -} - -private fun TypeName.generateKotlinConstructorIfNecessary( - paramName: String, - isForTuple: Boolean = true, -): String { - val unwrapped = this.firstTypeArgument - return when { - unwrapped != null -> unwrapped.generateKotlinConstructorForNullableType(paramName) - this.optional && !isForTuple -> this.generateKotlinConstructorForNullableType(paramName) - else -> generateKotlinConstructorForNonNullableType(paramName) - }.let { - if (!isForTuple) { - it - } else if (this == STRING) { - it.replace(paramName, "$paramName as NSString") - } else if (unwrapped == STRING) { - it.replace("? $paramName :", "? $paramName! as NSString :") - } else { - it - } - } -} - -private fun TypeName.getKotlinInteropFromSwiftType(): String? = - swiftTypeToKotlinMap[this]?.replace("kotlin/", "Kotlin") - -private val TypeName.swiftRetriver: String - get() = (if (!this.optional) "!" else "?") - .plus(".") - .plus( - this.name.split(".").last().lowercase() - .replace("?", "") - .let { - when (it) { - "float32" -> "float" - "float64" -> "double" - else -> it - } - }, - ) - .plus("Value") - -private fun TypeName.generateSwiftRetrieverForKotlinType( - paramName: String, - isForTuple: Boolean = true, -): String = - if (swiftTypeToKotlinMap.containsKey(this) || swiftOptionalTypeToKotlinMap.containsKey(this)) { - "$paramName" - .plus( - if (isForTuple || this.optional) { - this.swiftRetriver - } else { - "" - }, - ) - } else if (this == STRING) { - "$paramName${if (isForTuple) "!" else ""} as String" - } else if (this == STRING.wrapOptional()) { - "$paramName != nil ? $paramName! as String : nil" - } else { - "$paramName${if (!this.optional && isForTuple) "!" else ""}" - } - -private fun TypeName.generateKotlinConstructorForNonNullableType(paramName: String): String { - return this.getKotlinInteropFromSwiftType()?.plus("(value: $paramName)") - ?: paramName -} - -private fun TypeName.generateKotlinConstructorForNullableType(paramName: String): String { - return "$paramName != nil ? " - .plus( - this.getKotlinInteropFromSwiftType()?.plus("(value: $paramName!)") - ?: paramName, - ) - .plus(" : nil") -} - -private fun List.stripInnerGenerics(): List = map { - (it as? ParameterizedTypeName)?.let { - if (it.rawType.simpleName.contains("NS")) it.rawType else null - } ?: it -} - -private val kotlinToSwiftTypeMap: Map = mapOf( - "kotlin/Any" to ANY_OBJECT, - "kotlin/Boolean" to BOOL, - "kotlin/Byte" to INT8, - "kotlin/Double" to FLOAT64, - "kotlin/Float" to FLOAT32, - "kotlin/Int" to INT32, - "kotlin/Long" to INT64, - "kotlin/Short" to INT16, - "kotlin/UByte" to UINT8, - "kotlin/UInt" to UINT32, - "kotlin/ULong" to UINT64, - "kotlin/UShort" to UIN16, -) - -val swiftTypeToKotlinMap: Map = mapOf( - ANY_OBJECT to "kotlin/Any", - BOOL to "kotlin/Boolean", - INT8 to "kotlin/Byte", - FLOAT64 to "kotlin/Double", - FLOAT32 to "kotlin/Float", - INT32 to "kotlin/Int", - INT64 to "kotlin/Long", - INT16 to "kotlin/Short", - UINT8 to "kotlin/UByte", - UINT32 to "kotlin/UInt", - UINT64 to "kotlin/ULong", - UIN16 to "kotlin/UShort", -) - -val swiftOptionalTypeToKotlinMap: Map = - swiftTypeToKotlinMap.map { (swiftType, kotlinName) -> - swiftType.wrapOptional() to kotlinName - } - .toMap() - -private fun String.kotlinPrimitiveTypeNameToSwift( - moduleName: String, - arguments: List, -): TypeName { - require(this.startsWith("kotlin/")) - return when (this) { - "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") - "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") - "kotlin/Pair" -> arguments.generateTupleType(moduleName) - "kotlin/Result" -> ANY_OBJECT - "kotlin/String" -> STRING - "kotlin/Triple" -> arguments.generateTupleType(moduleName) - "kotlin/Throwable" -> DeclaredTypeName( - moduleName = moduleName, - simpleName = "KotlinThrowable", - ) - - "kotlin/Unit" -> VOID - "kotlin/collections/List" -> ARRAY - "kotlin/collections/Map" -> DICTIONARY - "kotlin/collections/Set" -> SET - else -> { - if (this.startsWith("kotlin/Function")) { - val typedArgs = arguments.getTypes(moduleName, NamingMode.KOTLIN, false) - val types = typedArgs.map { ParameterSpec.unnamed(it) }.dropLast(1) - FunctionTypeName.get(types, typedArgs.last()) - } else { - kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) - } - } - } -} - -private fun List.generateTupleType(moduleName: String): TupleTypeName = - TupleTypeName.of( - *this - .map { projection -> - (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) - .let { - if (projection.type?.isNullable == true && !it.optional) { - it.wrapOptional() - } else { - it - } - } - } - .map { "" to it } - .toTypedArray(), - ) - -private fun KmType.kotlinPrimitiveTypeNameToSwift(moduleName: String): TypeName? { - val typeName = this.nameAsString - - return when { - typeName == null -> null - typeName.startsWith("kotlin/") -> - typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments) - - else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) - }?.addGenericsAndOptional( - kmType = this, - moduleName = moduleName, - namingMode = null, - isOuterSwift = true, - ) -} - -private val NSSTRING = DeclaredTypeName(moduleName = "Foundation", simpleName = "NSString") - -private fun String.kotlinPrimitiveTypeNameToObjectiveC(moduleName: String): DeclaredTypeName { - require(this.startsWith("kotlin/")) - return when (this) { - "kotlin/Any" -> ANY_OBJECT - "kotlin/Boolean" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinBoolean") - "kotlin/Pair" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinPair") - "kotlin/Result" -> ANY_OBJECT - "kotlin/String" -> NSSTRING - "kotlin/Short" -> DeclaredTypeName(moduleName = "Foundation", simpleName = "NSNumber") - "kotlin/Triple" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinTriple") - "kotlin/collections/Map" -> DeclaredTypeName( - moduleName = "Foundation", - simpleName = "NSDictionary", - ) - - "kotlin/collections/Set" -> DeclaredTypeName( - moduleName = "Foundation", - simpleName = "NSSet", - ) - - "kotlin/collections/List" -> DeclaredTypeName( - moduleName = "Foundation", - simpleName = "NSArray", - ) - - else -> this.kotlinInteropName(moduleName) - } -} - -private fun String.kotlinInteropName(moduleName: String) = DeclaredTypeName( - moduleName = moduleName, - simpleName = "Kotlin" + this.split("/").last(), -) - -private fun getDeclaredTypeNameFromNonPrimitive( - typeName: String, - moduleName: String, -) = if (typeName.startsWith("platform/")) { - val withoutCompanion: String = typeName.removeSuffix(".Companion") - val moduleAndClass: List = withoutCompanion.split("/").drop(1) - val module: String = moduleAndClass[0] - val className: String = moduleAndClass[1] - - DeclaredTypeName.typeName( - listOf(module, className).joinToString("."), - ).objcNameToSwift() -} else { - // take type after final slash and generate declared type assuming module name - val simpleName: String = typeName.split("/").last() - DeclaredTypeName( - moduleName = moduleName, - simpleName = simpleName, - ) -} - -private fun TypeName.addGenericsAndOptional( - kmType: KmType, - moduleName: String, - namingMode: NamingMode?, - isOuterSwift: Boolean, -): TypeName { - val isSwift = (this as? DeclaredTypeName)?.moduleName == "Swift" - - return if (this is DeclaredTypeName && kmType.hasGenerics) { - val genericTypes = kmType.arguments.getTypes( - moduleName = moduleName, - namingMode = when { - this.simpleName.startsWith("Kotlin") -> NamingMode.KOTLIN_NO_STRING - this == ARRAY || this == SET || this == DICTIONARY -> NamingMode.KOTLIN - namingMode != null -> namingMode - isSwift -> NamingMode.SWIFT - else -> NamingMode.OBJC - }, - isOuterSwift = isSwift, - ) - this.parameterizedBy(*genericTypes.toTypedArray()) - } else { - this - }.let { - if (kmType.isNullable && isOuterSwift) it.makeOptional() else it - } -} - -enum class NamingMode { KOTLIN, KOTLIN_NO_STRING, SWIFT, OBJC } - -private fun List.getTypes( - moduleName: String, - namingMode: NamingMode, - isOuterSwift: Boolean, -): List = this.map { - it.type?.kotlinTypeNameToInner(moduleName, namingMode, isOuterSwift) ?: ANY_OBJECT -} - -private fun KmType.kotlinTypeNameToInner( - moduleName: String, - namingMode: NamingMode, - isOuterSwift: Boolean, -): TypeName? { - val typeName = this.nameAsString - return when { - typeName == null -> null - typeName.startsWith("kotlin/") -> { - when (namingMode) { - NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) - NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) - NamingMode.KOTLIN_NO_STRING -> - typeName - .kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - .let { if (it == STRING) NSSTRING else it } - } - } - - else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) - }?.addGenericsAndOptional( - kmType = this, - moduleName = moduleName, - namingMode = namingMode, - isOuterSwift = isOuterSwift, - ) -} - -private fun String.kotlinPrimitiveTypeNameToKotlinInterop(moduleName: String): TypeName { - require(this.startsWith("kotlin/")) - return when (this) { - "kotlin/String" -> STRING - "kotlin/collections/List" -> ARRAY - "kotlin/collections/Map" -> DICTIONARY - "kotlin/collections/Set" -> SET - else -> this.kotlinInteropName(moduleName) - } -} - -val TypeName.isCharacter: Boolean - get() = this.name == "Swift.Character" - -private val KmType.isNullable: Boolean - get() = Flag.Type.IS_NULLABLE(flags) - -private val KmType.hasGenerics: Boolean - get() = this.arguments.isNotEmpty() - -private val KmType.nameAsString: String? - get() { - val classifier = this.classifier - return when (classifier) { - is KmClassifier.Class -> classifier.name - is KmClassifier.TypeParameter -> null - is KmClassifier.TypeAlias -> classifier.name - } - } - -private fun TypeName.generateInitParameter(paramName: String): String { - return "$paramName: " - .plus( - this.generateSwiftRetrieverForKotlinType( - paramName = "obj.$paramName", - isForTuple = false, - ), - ) -} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftEnumFeature.kt index 7994873..9cfa71f 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftEnumFeature.kt @@ -26,7 +26,7 @@ import kotlin.reflect.KClass class SealedToSwiftEnumFeature( override val featureContext: KClass, - override val filter: Filter + override val filter: Filter, ) : ProcessorFeature() { @Suppress("ReturnCount") @@ -59,15 +59,15 @@ class SealedToSwiftEnumFeature( kotlinFrameworkName = kotlinFrameworkName, sealedCases = sealedCases, className = className, - originalClassName = originalClassName - ) + originalClassName = originalClassName, + ), ) .addProperty( buildSealedProperty( featureContext = featureContext, kotlinFrameworkName = kotlinFrameworkName, - sealedCases = sealedCases - ) + sealedCases = sealedCases, + ), ) .build() @@ -79,7 +79,7 @@ class SealedToSwiftEnumFeature( kotlinFrameworkName: String, sealedCases: List, className: String, - originalClassName: String + originalClassName: String, ): FunctionSpec { return FunctionSpec.builder("init") .addModifiers(Modifier.PUBLIC) @@ -88,8 +88,8 @@ class SealedToSwiftEnumFeature( name = "obj", type = featureContext.clazz.getDeclaredTypeNameWithGenerics( kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses - ) + classes = featureContext.kLibClasses, + ), ) .addCode( CodeBlock.builder() @@ -117,14 +117,14 @@ class SealedToSwiftEnumFeature( unindent() add("}\n") } - .build() + .build(), ) .build() } private fun buildEnumCases( kotlinFrameworkName: String, - featureContext: ClassContext + featureContext: ClassContext, ): List { val kmClass = featureContext.clazz return kmClass.sealedSubclasses.mapNotNull { sealedClassName -> @@ -141,18 +141,20 @@ class SealedToSwiftEnumFeature( kotlinFrameworkName: String, featureContext: ClassContext, subclassName: ClassName, - sealedCaseClass: KmClass + sealedCaseClass: KmClass, ): EnumCase { val kmClass = featureContext.clazz val name: String = if (subclassName.startsWith(kmClass.name)) { subclassName.removePrefix(kmClass.name).removePrefix(".") - } else subclassName.removePrefix(kmClass.name.substringBeforeLast("/")).removePrefix("/") + } else { + subclassName.removePrefix(kmClass.name.substringBeforeLast("/")).removePrefix("/") + } val decapitalizedName: String = name.decapitalize(Locale.ROOT) val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) val caseArg = sealedCaseClass.getDeclaredTypeNameWithGenerics( kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses + classes = featureContext.kLibClasses, ) return EnumCase( @@ -165,18 +167,18 @@ class SealedToSwiftEnumFeature( }, initBlock = if (isObject) "" else "(obj)", caseArg = caseArg, - caseBlock = if (isObject) "" else "(let obj)" + caseBlock = if (isObject) "" else "(let obj)", ) } private fun buildSealedProperty( featureContext: ClassContext, kotlinFrameworkName: String, - sealedCases: List + sealedCases: List, ): PropertySpec { val returnType: TypeName = featureContext.clazz.getDeclaredTypeNameWithGenerics( kotlinFrameworkName = kotlinFrameworkName, - classes = featureContext.kLibClasses + classes = featureContext.kLibClasses, ) return PropertySpec.builder("sealed", type = returnType) .addModifiers(Modifier.PUBLIC) @@ -184,13 +186,13 @@ class SealedToSwiftEnumFeature( FunctionSpec .getterBuilder() .addCode(buildSealedPropertyBody(sealedCases, returnType)) - .build() + .build(), ).build() } private fun buildSealedPropertyBody( sealedCases: List, - returnType: TypeName + returnType: TypeName, ): CodeBlock = CodeBlock.builder().apply { add("switch self {\n") sealedCases.forEach { enumCase -> @@ -209,7 +211,7 @@ class SealedToSwiftEnumFeature( private fun CodeBlock.Builder.addSealedCaseReturnCode( enumCase: EnumCase, - returnType: TypeName + returnType: TypeName, ) { val paramType: TypeName? = enumCase.param val cast: String @@ -219,7 +221,7 @@ class SealedToSwiftEnumFeature( returnedName = "${enumCase.caseArg}()" cast = if (returnType is ParameterizedTypeName) { // The return type is generic and there is no parameter, so it can - // be assumed that the case is NOT generic. Thus the case needs to + // be assumed that the case is NOT generic. Thus, the case needs to // be force-cast. "as!" } else { @@ -233,7 +235,7 @@ class SealedToSwiftEnumFeature( cast = if (paramType is ParameterizedTypeName && returnType is ParameterizedTypeName) { if (paramType.typeArguments == returnType.typeArguments) { // The parameter and return type have the same generic pattern. This - // is true if both are NOT generic OR if both are generic. Thus a + // is true if both are NOT generic OR if both are generic. Thus, a // regular cast can be used. "as" } else { @@ -254,7 +256,7 @@ class SealedToSwiftEnumFeature( val initCheck: String, val initBlock: String, val caseArg: TypeName, - val caseBlock: String + val caseBlock: String, ) { val enumCaseSpec: EnumerationCaseSpec get() { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt new file mode 100644 index 0000000..e121212 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -0,0 +1,162 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import io.outfoxx.swiftpoet.ARRAY +import io.outfoxx.swiftpoet.DICTIONARY +import io.outfoxx.swiftpoet.DeclaredTypeName +import io.outfoxx.swiftpoet.EnumerationCaseSpec +import io.outfoxx.swiftpoet.ParameterizedTypeName +import io.outfoxx.swiftpoet.SET +import io.outfoxx.swiftpoet.TupleTypeName +import io.outfoxx.swiftpoet.TypeName +import io.outfoxx.swiftpoet.parameterizedBy +import kotlinx.metadata.KmValueParameter + +data class AssociatedEnumCase( + val frameworkName: String, + val name: String, + val param: TypeName?, + val initCheck: String, + val caseArg: TypeName, + val isObject: Boolean, + val constructorParams: List, +) { + private val explodedParams: List> = constructorParams.map { + Pair( + it.name, + it.type?.kotlinPrimitiveTypeNameToSwift(frameworkName) + ?: DeclaredTypeName.typeName("Swift.FailedToGetReturnType"), + ) + } + + internal val initBlock: String = if (isObject) { + "" + } else { + "(" + .plus( + explodedParams.joinToString(",\n") { + val tupleType = it.second as? TupleTypeName + val paramType = (it.second as? ParameterizedTypeName) + when { + tupleType != null -> tupleType.generateTuple(it.first) + it.second.isCharacter -> "${it.first}: Character(UnicodeScalar(obj.${it.first})!)" + paramType?.rawType == DICTIONARY -> paramType.toDictionaryCaster( + it.first, + ) + + paramType?.rawType == SET -> paramType.toSetCaster(it.first) + paramType?.rawType == ARRAY -> paramType.toArrayCaster(it.first) + paramType?.optional == true -> { + val unwrapped = paramType.unwrapOptional() + when ((unwrapped as? ParameterizedTypeName)?.rawType) { + DICTIONARY -> { + // TODO: unwrapped for other classes? + unwrapped.toDictionaryCaster(it.first, true) + } + SET -> paramType.toSetCaster(it.first, true) + ARRAY -> paramType.toArrayCaster(it.first, true) + else -> paramType.generateInitParameter(it.first) + } + } + + else -> it.second.generateInitParameter(it.first) + } + }, + ) + .plus(")") + } + + internal val caseBlock = if (isObject) { + "" + } else { + "(" + explodedParams.joinToString { "let ${it.first}" } + ")" + } + internal val enumCaseSpec: EnumerationCaseSpec + get() { + return if (param == null) { + EnumerationCaseSpec.builder(name).build() + } else if (explodedParams.isNotEmpty()) { + val stripGenericsFromObjC = explodedParams.map { param -> + (param.second as? ParameterizedTypeName)?.let { + if (it.rawType.moduleName != "Swift") { + param.first to it.rawType.parameterizedBy( + *it.typeArguments.stripInnerGenerics().toTypedArray(), + ) + } else { + null + } + } ?: param + } + EnumerationCaseSpec.builder( + name = name, + type = TupleTypeName.of(*stripGenericsFromObjC.toTypedArray()), + ).build() + } else { + EnumerationCaseSpec.builder(name, param).build() + } + } + + internal val swiftToKotlinConstructor: String = explodedParams.joinToString { (paramName, paramType) -> + "$paramName: " + when { + paramType.isCharacter -> "$paramName.utf16.first!" + paramType is TupleTypeName -> { + if (paramType.types.size == 2) { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + + "KotlinPair<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(")") + } else if (paramType.types.size == 3) { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + val third = paramType.types[2] + val thirdType = third.second + "KotlinTriple<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(thirdType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(", third: ") + .plus( + thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), + ) + .plus(")") + } else { + "unknown tuple type" + } + } + + else -> paramType.generateKotlinConstructorIfNecessaryForParameter(paramName) + } + } +} + +private fun String.toNSString(): String = + if (this == "String" || this == "Swift.String") { + "NSString" + } else { + this + } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt new file mode 100644 index 0000000..296aeb8 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt @@ -0,0 +1,64 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import dev.icerock.moko.kswift.plugin.context.ClassContext +import dev.icerock.moko.kswift.plugin.context.kLibClasses +import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics +import kotlinx.metadata.ClassName +import kotlinx.metadata.Flag +import kotlinx.metadata.KmClass +import java.util.Locale + +fun buildEnumCases( + kotlinFrameworkName: String, + featureContext: ClassContext, +): List { + val kmClass = featureContext.clazz + return kmClass.sealedSubclasses.mapNotNull { sealedClassName -> + val sealedClass: KmClass = featureContext.parentContext + .fragment.classes.first { it.name == sealedClassName } + + if (Flag.IS_PUBLIC(sealedClass.flags).not()) return@mapNotNull null + + buildEnumCase( + kotlinFrameworkName = kotlinFrameworkName, + featureContext = featureContext, + subclassName = sealedClassName, + sealedCaseClass = sealedClass, + ) + } +} + +private fun buildEnumCase( + kotlinFrameworkName: String, + featureContext: ClassContext, + subclassName: ClassName, + sealedCaseClass: KmClass, +): AssociatedEnumCase { + val kmClass = featureContext.clazz + val name: String = if (subclassName.startsWith(kmClass.name)) { + subclassName.removePrefix(kmClass.name).removePrefix(".") + } else { + subclassName.removePrefix(kmClass.name.substringBeforeLast("/")).removePrefix("/") + } + val decapitalizedName: String = name.decapitalize(Locale.ROOT) + + val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) + val caseArg = sealedCaseClass.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ) + + return AssociatedEnumCase( + frameworkName = kotlinFrameworkName, + name = decapitalizedName, + param = if (isObject) null else caseArg, + initCheck = if (isObject) { + "obj is $caseArg" + } else { + "let obj = obj as? $caseArg" + }, + caseArg = caseArg, + isObject = isObject, + constructorParams = sealedCaseClass.constructors.first().valueParameters, + ) +} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt new file mode 100644 index 0000000..808ba9c --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt @@ -0,0 +1,32 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import dev.icerock.moko.kswift.plugin.isNullable +import io.outfoxx.swiftpoet.ANY_OBJECT +import io.outfoxx.swiftpoet.TupleTypeName +import io.outfoxx.swiftpoet.TypeName +import kotlinx.metadata.KmTypeProjection + +internal fun List.generateTupleType(moduleName: String): TupleTypeName = + TupleTypeName.of( + *this + .map { projection -> + (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) + .let { + if (projection.type?.isNullable == true && !it.optional) { + it.wrapOptional() + } else { + it + } + } + } + .map { "" to it } + .toTypedArray(), + ) + +internal fun List.getTypes( + moduleName: String, + namingMode: NamingMode, + isOuterSwift: Boolean, +): List = this.map { + it.type?.kotlinTypeNameToInner(moduleName, namingMode, isOuterSwift) ?: ANY_OBJECT +} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/NamingMode.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/NamingMode.kt new file mode 100644 index 0000000..a3d1b8a --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/NamingMode.kt @@ -0,0 +1,3 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +internal enum class NamingMode { KOTLIN, KOTLIN_NO_STRING, SWIFT, OBJC } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt new file mode 100644 index 0000000..f9f9b23 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -0,0 +1,170 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import dev.icerock.moko.kswift.plugin.objcNameToSwift +import io.outfoxx.swiftpoet.ANY_OBJECT +import io.outfoxx.swiftpoet.ARRAY +import io.outfoxx.swiftpoet.DICTIONARY +import io.outfoxx.swiftpoet.DeclaredTypeName +import io.outfoxx.swiftpoet.FunctionTypeName +import io.outfoxx.swiftpoet.ParameterSpec +import io.outfoxx.swiftpoet.SET +import io.outfoxx.swiftpoet.STRING +import io.outfoxx.swiftpoet.TypeName +import io.outfoxx.swiftpoet.VOID +import kotlinx.metadata.KmClassifier +import kotlinx.metadata.KmType +import kotlinx.metadata.KmTypeProjection + +private val NSSTRING = DeclaredTypeName(moduleName = "Foundation", simpleName = "NSString") + +internal fun KmType.kotlinTypeNameToInner( + moduleName: String, + namingMode: NamingMode, + isOuterSwift: Boolean, +): TypeName? { + val typeName = this.nameAsString + return when { + typeName == null -> null + typeName.startsWith("kotlin/") -> { + when (namingMode) { + NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) + NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) + NamingMode.KOTLIN_NO_STRING -> + typeName + .kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + .let { if (it == STRING) NSSTRING else it } + } + } + + else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) + }?.addGenericsAndOptional( + kmType = this, + moduleName = moduleName, + namingMode = namingMode, + isOuterSwift = isOuterSwift, + ) +} + +private fun String.kotlinPrimitiveTypeNameToSwift( + moduleName: String, + arguments: List, +): TypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") + "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") + "kotlin/Pair" -> arguments.generateTupleType(moduleName) + "kotlin/Result" -> ANY_OBJECT + "kotlin/String" -> STRING + "kotlin/Triple" -> arguments.generateTupleType(moduleName) + "kotlin/Throwable" -> DeclaredTypeName( + moduleName = moduleName, + simpleName = "KotlinThrowable", + ) + + "kotlin/Unit" -> VOID + "kotlin/collections/List" -> ARRAY + "kotlin/collections/Map" -> DICTIONARY + "kotlin/collections/Set" -> SET + else -> { + if (this.startsWith("kotlin/Function")) { + val typedArgs = arguments.getTypes(moduleName, NamingMode.KOTLIN, false) + val types = typedArgs.map { ParameterSpec.unnamed(it) }.dropLast(1) + FunctionTypeName.get(types, typedArgs.last()) + } else { + kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) + } + } + } +} + +internal fun KmType.kotlinPrimitiveTypeNameToSwift(moduleName: String): TypeName? { + val typeName = this.nameAsString + + return when { + typeName == null -> null + typeName.startsWith("kotlin/") -> + typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments) + + else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) + }?.addGenericsAndOptional( + kmType = this, + moduleName = moduleName, + namingMode = null, + isOuterSwift = true, + ) +} + +private val KmType.nameAsString: String? + get() = when (val classifier = this.classifier) { + is KmClassifier.Class -> classifier.name + is KmClassifier.TypeParameter -> null + is KmClassifier.TypeAlias -> classifier.name + } + +private fun String.kotlinPrimitiveTypeNameToKotlinInterop(moduleName: String): TypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/String" -> STRING + "kotlin/collections/List" -> ARRAY + "kotlin/collections/Map" -> DICTIONARY + "kotlin/collections/Set" -> SET + else -> this.kotlinInteropName(moduleName) + } +} + +private fun String.kotlinInteropName(moduleName: String) = DeclaredTypeName( + moduleName = moduleName, + simpleName = "Kotlin" + this.split("/").last(), +) + +private fun String.kotlinPrimitiveTypeNameToObjectiveC(moduleName: String): DeclaredTypeName { + require(this.startsWith("kotlin/")) + return when (this) { + "kotlin/Any" -> ANY_OBJECT + "kotlin/Boolean" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinBoolean") + "kotlin/Pair" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinPair") + "kotlin/Result" -> ANY_OBJECT + "kotlin/String" -> NSSTRING + "kotlin/Short" -> DeclaredTypeName(moduleName = "Foundation", simpleName = "NSNumber") + "kotlin/Triple" -> DeclaredTypeName(moduleName = moduleName, simpleName = "KotlinTriple") + "kotlin/collections/Map" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSDictionary", + ) + + "kotlin/collections/Set" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSSet", + ) + + "kotlin/collections/List" -> DeclaredTypeName( + moduleName = "Foundation", + simpleName = "NSArray", + ) + + else -> this.kotlinInteropName(moduleName) + } +} + +private fun getDeclaredTypeNameFromNonPrimitive( + typeName: String, + moduleName: String, +) = if (typeName.startsWith("platform/")) { + val withoutCompanion: String = typeName.removeSuffix(".Companion") + val moduleAndClass: List = withoutCompanion.split("/").drop(1) + val module: String = moduleAndClass[0] + val className: String = moduleAndClass[1] + + DeclaredTypeName.typeName( + listOf(module, className).joinToString("."), + ).objcNameToSwift() +} else { + // take type after final slash and generate declared type assuming module name + val simpleName: String = typeName.split("/").last() + DeclaredTypeName( + moduleName = moduleName, + simpleName = simpleName, + ) +} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt new file mode 100644 index 0000000..252afa0 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt @@ -0,0 +1,55 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import io.outfoxx.swiftpoet.ParameterizedTypeName +import io.outfoxx.swiftpoet.TypeName + +internal fun ParameterizedTypeName.toArrayCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus("]") + .plus(if (optional) " : nil" else "") + +internal fun ParameterizedTypeName.toSetCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! Set<") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus(">") + .plus(if (optional) " : nil" else "") + +internal fun ParameterizedTypeName.toDictionaryCaster( + paramName: String, + optional: Boolean = false, +): String = + "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus(" : ") + .plus(this.typeArguments[1].kotlinInteropTypeWithFallback) + .plus("]") + .plus(if (optional) " : nil" else "") + +internal fun TypeName.generateInitParameter(paramName: String): String { + return "$paramName: " + .plus( + this.generateSwiftRetrieverForKotlinType( + paramName = "obj.$paramName", + isForTuple = false, + ), + ) +} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt new file mode 100644 index 0000000..c4d9918 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt @@ -0,0 +1,52 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import io.outfoxx.swiftpoet.ANY_OBJECT +import io.outfoxx.swiftpoet.BOOL +import io.outfoxx.swiftpoet.DeclaredTypeName +import io.outfoxx.swiftpoet.FLOAT32 +import io.outfoxx.swiftpoet.FLOAT64 +import io.outfoxx.swiftpoet.INT16 +import io.outfoxx.swiftpoet.INT32 +import io.outfoxx.swiftpoet.INT64 +import io.outfoxx.swiftpoet.INT8 +import io.outfoxx.swiftpoet.ParameterizedTypeName +import io.outfoxx.swiftpoet.UIN16 +import io.outfoxx.swiftpoet.UINT32 +import io.outfoxx.swiftpoet.UINT64 +import io.outfoxx.swiftpoet.UINT8 + +internal val kotlinToSwiftTypeMap: Map = mapOf( + "kotlin/Any" to ANY_OBJECT, + "kotlin/Boolean" to BOOL, + "kotlin/Byte" to INT8, + "kotlin/Double" to FLOAT64, + "kotlin/Float" to FLOAT32, + "kotlin/Int" to INT32, + "kotlin/Long" to INT64, + "kotlin/Short" to INT16, + "kotlin/UByte" to UINT8, + "kotlin/UInt" to UINT32, + "kotlin/ULong" to UINT64, + "kotlin/UShort" to UIN16, +) + +internal val swiftTypeToKotlinMap: Map = mapOf( + ANY_OBJECT to "kotlin/Any", + BOOL to "kotlin/Boolean", + INT8 to "kotlin/Byte", + FLOAT64 to "kotlin/Double", + FLOAT32 to "kotlin/Float", + INT32 to "kotlin/Int", + INT64 to "kotlin/Long", + INT16 to "kotlin/Short", + UINT8 to "kotlin/UByte", + UINT32 to "kotlin/UInt", + UINT64 to "kotlin/ULong", + UIN16 to "kotlin/UShort", +) + +internal val swiftOptionalTypeToKotlinMap: Map = + swiftTypeToKotlinMap.map { (swiftType, kotlinName) -> + swiftType.wrapOptional() to kotlinName + } + .toMap() diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TupleTypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TupleTypeNameExt.kt new file mode 100644 index 0000000..bd442e4 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TupleTypeNameExt.kt @@ -0,0 +1,45 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import io.outfoxx.swiftpoet.TupleTypeName + +internal fun TupleTypeName.generateTuple(paramName: String): String = + if (this.types.size == 2) { + "$paramName: (" + .plus( + this.types[0].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.first", + ), + ) + .plus(", ") + .plus( + this.types[1].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.second", + ), + ) + .plus(")") + } else { + "$paramName: (" + .plus( + this.types[0].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.first", + ), + ) + .plus(", ") + .plus( + this.types[1].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.second", + ), + ) + .plus(", ") + .plus( + this.types[2].second + .generateSwiftRetrieverForKotlinType( + "obj.$paramName.third", + ), + ) + .plus(")") + } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt new file mode 100644 index 0000000..f20ac06 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt @@ -0,0 +1,144 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import dev.icerock.moko.kswift.plugin.hasGenerics +import dev.icerock.moko.kswift.plugin.isNullable +import io.outfoxx.swiftpoet.ARRAY +import io.outfoxx.swiftpoet.DICTIONARY +import io.outfoxx.swiftpoet.DeclaredTypeName +import io.outfoxx.swiftpoet.ParameterizedTypeName +import io.outfoxx.swiftpoet.SET +import io.outfoxx.swiftpoet.STRING +import io.outfoxx.swiftpoet.TypeName +import io.outfoxx.swiftpoet.parameterizedBy +import kotlinx.metadata.KmType + +internal val TypeName.isCharacter: Boolean + get() = this.name == "Swift.Character" + +internal val TypeName.kotlinInteropTypeWithFallback: String + get() = ( + this.firstTypeArgument?.kotlinInteropFromSwiftType + ?: this.kotlinInteropFromSwiftType + ?: this.name + ) + .replace("?", "") + +internal fun TypeName.generateKotlinConstructorIfNecessaryForParameter(paramName: String): String { + return when { + this.optional -> this.generateKotlinConstructorIfNecessary(paramName, false) + else -> paramName + } +} + +internal fun TypeName.generateKotlinConstructorIfNecessary( + paramName: String, + isForTuple: Boolean = true, +): String { + val unwrapped = this.firstTypeArgument + return when { + unwrapped != null -> unwrapped.generateKotlinConstructorForNullableType(paramName) + this.optional && !isForTuple -> this.generateKotlinConstructorForNullableType(paramName) + else -> generateKotlinConstructorForNonNullableType(paramName) + }.let { + if (!isForTuple) { + it + } else if (this == STRING) { + it.replace(paramName, "$paramName as NSString") + } else if (unwrapped == STRING) { + it.replace("? $paramName :", "? $paramName! as NSString :") + } else { + it + } + } +} + +internal fun List.stripInnerGenerics(): List = map { typeName -> + (typeName as? ParameterizedTypeName)?.let { + if (it.rawType.simpleName.contains("NS")) it.rawType else null + } ?: typeName +} + +internal fun TypeName.addGenericsAndOptional( + kmType: KmType, + moduleName: String, + namingMode: NamingMode?, + isOuterSwift: Boolean, +): TypeName { + val isSwift = (this as? DeclaredTypeName)?.moduleName == "Swift" + + return if (this is DeclaredTypeName && kmType.hasGenerics) { + val genericTypes = kmType.arguments.getTypes( + moduleName = moduleName, + namingMode = when { + this.simpleName.startsWith("Kotlin") -> NamingMode.KOTLIN_NO_STRING + this == ARRAY || this == SET || this == DICTIONARY -> NamingMode.KOTLIN + namingMode != null -> namingMode + isSwift -> NamingMode.SWIFT + else -> NamingMode.OBJC + }, + isOuterSwift = isSwift, + ) + this.parameterizedBy(*genericTypes.toTypedArray()) + } else { + this + }.let { + if (kmType.isNullable && isOuterSwift) it.makeOptional() else it + } +} + +private val TypeName.firstTypeArgument: TypeName? + get() = (this as? ParameterizedTypeName)?.typeArguments?.first() + +private val TypeName.kotlinInteropFromSwiftType: String? + get() = swiftTypeToKotlinMap[this]?.replace("kotlin/", "Kotlin") + +private val TypeName.swiftRetriever: String + get() = (if (!this.optional) "!" else "?") + .plus(".") + .plus( + this.name.split(".").last().lowercase() + .replace("?", "") + .let { + when (it) { + "float32" -> "float" + "float64" -> "double" + else -> it + } + }, + ) + .plus("Value") + +internal fun TypeName.generateSwiftRetrieverForKotlinType( + paramName: String, + isForTuple: Boolean = true, +): String = + if (swiftTypeToKotlinMap.containsKey(this) || swiftOptionalTypeToKotlinMap.containsKey(this)) { + paramName + .plus( + if (isForTuple || this.optional) { + this.swiftRetriever + } else { + "" + }, + ) + } else if (this == STRING) { + "$paramName${if (isForTuple) "!" else ""} as String" + } else if (this == STRING.wrapOptional()) { + "$paramName != nil ? $paramName! as String : nil" + } else { + "$paramName${if (!this.optional && isForTuple) "!" else ""}" + } + +private fun TypeName.generateKotlinConstructorForNonNullableType(paramName: String): String { + return this.kotlinInteropFromSwiftType?.plus("(value: $paramName)") + ?: paramName +} + +private fun TypeName.generateKotlinConstructorForNullableType(paramName: String): String { + return "$paramName != nil ? " + .plus( + this.kotlinInteropFromSwiftType?.plus("(value: $paramName!)") + ?: paramName, + ) + .plus(" : nil") +} diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt new file mode 100644 index 0000000..22d4b27 --- /dev/null +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt @@ -0,0 +1,143 @@ +package dev.icerock.moko.kswift.plugin.feature.associatedenum + +import dev.icerock.moko.kswift.plugin.context.ClassContext +import dev.icerock.moko.kswift.plugin.context.kLibClasses +import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics +import io.outfoxx.swiftpoet.CodeBlock +import io.outfoxx.swiftpoet.FunctionSpec +import io.outfoxx.swiftpoet.Modifier +import io.outfoxx.swiftpoet.PropertySpec +import io.outfoxx.swiftpoet.TypeName +import io.outfoxx.swiftpoet.TypeSpec +import io.outfoxx.swiftpoet.TypeVariableName +import org.gradle.configurationcache.extensions.capitalized + +@Suppress("LongParameterList") +fun buildTypeSpec( + className: String, + featureContext: ClassContext, + typeVariables: List, + sealedCases: List, + kotlinFrameworkName: String, + originalClassName: String, +): TypeSpec { + val enumType: TypeSpec = TypeSpec.enumBuilder(className) + .addDoc("selector: ${featureContext.prefixedUniqueId}") + .apply { + typeVariables.forEach { addTypeVariable(it) } + sealedCases.forEach { addEnumCase(it.enumCaseSpec) } + } + .addModifiers(Modifier.PUBLIC) + .addFunction( + buildEnumConstructor( + featureContext = featureContext, + kotlinFrameworkName = kotlinFrameworkName, + sealedCases = sealedCases, + className = className, + originalClassName = originalClassName, + ), + ) + .addProperty( + buildSealedProperty( + featureContext = featureContext, + kotlinFrameworkName = kotlinFrameworkName, + sealedCases = sealedCases, + ), + ) + .build() + return enumType +} + +private fun buildEnumConstructor( + featureContext: ClassContext, + kotlinFrameworkName: String, + sealedCases: List, + className: String, + originalClassName: String, +): FunctionSpec { + return FunctionSpec.builder("init") + .addModifiers(Modifier.PUBLIC) + .addParameter( + label = "_", + name = "obj", + type = featureContext.clazz.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ), + ) + .addCode( + CodeBlock.builder() + .apply { + sealedCases.forEachIndexed { index, enumCase -> + buildString { + if (index != 0) append("} else ") + append("if ") + append(enumCase.initCheck) + append(" {") + append('\n') + }.also { add(it) } + indent() + buildString { + append("self = .") + append(enumCase.name) + append(enumCase.initBlock) + append('\n') + }.also { add(it) } + unindent() + } + add("} else {\n") + indent() + add( + "fatalError(\"$className not synchronized with $originalClassName class\")\n", + ) + unindent() + add("}\n") + } + .build(), + ) + .build() +} + +private fun buildSealedProperty( + featureContext: ClassContext, + kotlinFrameworkName: String, + sealedCases: List, +): PropertySpec { + val returnType: TypeName = featureContext.clazz.getDeclaredTypeNameWithGenerics( + kotlinFrameworkName = kotlinFrameworkName, + classes = featureContext.kLibClasses, + ) + return PropertySpec.builder("sealed", type = returnType) + .addModifiers(Modifier.PUBLIC) + .getter( + FunctionSpec + .getterBuilder() + .addCode(buildSealedPropertyBody(sealedCases)) + .build(), + ).build() +} + +private fun buildSealedPropertyBody( + sealedCases: List, +): CodeBlock = CodeBlock.builder().apply { + add("switch self {\n") + sealedCases.forEach { enumCase -> + buildString { + append("case .") + append(enumCase.name) + append(enumCase.caseBlock) + append(":\n") + }.also { add(it) } + indent() + addSealedCaseReturnCode(enumCase) + unindent() + } + add("}\n") +}.build() + +private fun CodeBlock.Builder.addSealedCaseReturnCode( + enumCase: AssociatedEnumCase, +) { + val parameters = enumCase.swiftToKotlinConstructor + add("return ${enumCase.name.capitalized()}($parameters)\n") +} From 81c8cdd461003c96df87415fe8f34948ca8d1ad0 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Tue, 21 Feb 2023 10:03:03 -0800 Subject: [PATCH 05/16] added gradle logger --- .../SealedToSwiftAssociatedEnumFeature.kt | 21 ++++++++++--------- .../associatedenum/AssociatedEnumCase.kt | 1 + .../AssociatedEnumCaseBuilder.kt | 6 ++---- .../feature/associatedenum/TypeSpecBuilder.kt | 3 ++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index 1d5c47b..15be0d4 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -9,9 +9,10 @@ import dev.icerock.moko.kswift.plugin.feature.associatedenum.buildTypeSpec import dev.icerock.moko.kswift.plugin.getSimpleName import io.outfoxx.swiftpoet.FileSpec import io.outfoxx.swiftpoet.TypeSpec -import io.outfoxx.swiftpoet.TypeVariableName import kotlinx.metadata.Flag import kotlinx.metadata.KmClass +import org.gradle.api.logging.Logger +import org.gradle.api.logging.Logging import kotlin.reflect.KClass class SealedToSwiftAssociatedEnumFeature( @@ -43,19 +44,17 @@ class SealedToSwiftAssociatedEnumFeature( if (featureContext.clazz.sealedSubclasses.isEmpty()) return - println("Generating enum for sealed class $originalClassName") - val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) - if (sealedCases.isEmpty()) return - - val typeVariables: List = - kmClass.buildTypeVariableNames(kotlinFrameworkName) + if (sealedCases.isEmpty()) { + logger.debug("No public subclasses found for sealed class $originalClassName") + return + } else { + logger.debug("Generating enum for sealed class $originalClassName (${sealedCases.size} public subclasses)") + } - val className: String = originalClassName.replace(".", "").plus("Ks") val enumType: TypeSpec = buildTypeSpec( - className = className, featureContext = featureContext, - typeVariables = typeVariables, + typeVariables = kmClass.buildTypeVariableNames(kotlinFrameworkName), sealedCases = sealedCases, kotlinFrameworkName = kotlinFrameworkName, originalClassName = originalClassName, @@ -78,5 +77,7 @@ class SealedToSwiftAssociatedEnumFeature( @JvmStatic override val factory = Companion + + val logger: Logger = Logging.getLogger("SealedToSwiftAssociatedEnumFeature") } } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index e121212..8c3fe76 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -70,6 +70,7 @@ data class AssociatedEnumCase( } else { "(" + explodedParams.joinToString { "let ${it.first}" } + ")" } + internal val enumCaseSpec: EnumerationCaseSpec get() { return if (param == null) { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt index 296aeb8..d00c745 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt @@ -11,9 +11,8 @@ import java.util.Locale fun buildEnumCases( kotlinFrameworkName: String, featureContext: ClassContext, -): List { - val kmClass = featureContext.clazz - return kmClass.sealedSubclasses.mapNotNull { sealedClassName -> +): List = featureContext.clazz.sealedSubclasses + .mapNotNull { sealedClassName -> val sealedClass: KmClass = featureContext.parentContext .fragment.classes.first { it.name == sealedClassName } @@ -26,7 +25,6 @@ fun buildEnumCases( sealedCaseClass = sealedClass, ) } -} private fun buildEnumCase( kotlinFrameworkName: String, diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt index 22d4b27..4a1d391 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt @@ -14,13 +14,14 @@ import org.gradle.configurationcache.extensions.capitalized @Suppress("LongParameterList") fun buildTypeSpec( - className: String, featureContext: ClassContext, typeVariables: List, sealedCases: List, kotlinFrameworkName: String, originalClassName: String, ): TypeSpec { + val className: String = originalClassName.replace(".", "").plus("Ks") + val enumType: TypeSpec = TypeSpec.enumBuilder(className) .addDoc("selector: ${featureContext.prefixedUniqueId}") .apply { From 199f494b36a7a102311439e3ae8ea91328f336fa Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 5 Mar 2023 06:30:10 -0800 Subject: [PATCH 06/16] remove spread operators --- gradle/libs.versions.toml | 2 +- .../icerock/moko/kswift/plugin/KmClassExt.kt | 2 +- .../icerock/moko/kswift/plugin/KmTypeExt.kt | 2 +- .../SealedToSwiftAssociatedEnumFeature.kt | 4 ++-- .../associatedenum/AssociatedEnumCase.kt | 4 ++-- .../associatedenum/KmTypeProjectionExt.kt | 24 +++++++++---------- .../feature/associatedenum/TypeNameExt.kt | 2 +- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4a5ccbf..f6724a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mokoResourcesVersion = "0.16.2" kotlinxMetadataKLibVersion = "0.0.1" kotlinPoetVersion = "1.6.0" -swiftPoetVersion = "1.5.1.0" +swiftPoetVersion = "1.5.1.1" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt index e530b34..53c6869 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt @@ -35,7 +35,7 @@ fun KmClass.getDeclaredTypeNameWithGenerics( return getDeclaredTypeName(kotlinFrameworkName, classes) .let { type -> if (haveGenerics.not() || isInterface) type - else type.parameterizedBy(*typeVariables.toTypedArray()) + else type.parameterizedBy(typeVariables) } } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt index cf2b463..0a9c268 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt @@ -132,7 +132,7 @@ fun KmType.kotlinTypeToTypeName( ) } @Suppress("SpreadOperator") - typeName.parameterizedBy(*arguments.toTypedArray()) + typeName.parameterizedBy(arguments) } } } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index 15be0d4..c971559 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -46,10 +46,10 @@ class SealedToSwiftAssociatedEnumFeature( val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) if (sealedCases.isEmpty()) { - logger.debug("No public subclasses found for sealed class $originalClassName") + logger.warn("No public subclasses found for sealed class $originalClassName") return } else { - logger.debug("Generating enum for sealed class $originalClassName (${sealedCases.size} public subclasses)") + logger.lifecycle("Generating enum for sealed class $originalClassName (${sealedCases.size} public subclasses)") } val enumType: TypeSpec = buildTypeSpec( diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index 8c3fe76..28c9e01 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -80,7 +80,7 @@ data class AssociatedEnumCase( (param.second as? ParameterizedTypeName)?.let { if (it.rawType.moduleName != "Swift") { param.first to it.rawType.parameterizedBy( - *it.typeArguments.stripInnerGenerics().toTypedArray(), + it.typeArguments.stripInnerGenerics(), ) } else { null @@ -89,7 +89,7 @@ data class AssociatedEnumCase( } EnumerationCaseSpec.builder( name = name, - type = TupleTypeName.of(*stripGenericsFromObjC.toTypedArray()), + type = TupleTypeName.of(stripGenericsFromObjC), ).build() } else { EnumerationCaseSpec.builder(name, param).build() diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt index 808ba9c..6522c06 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt @@ -7,20 +7,18 @@ import io.outfoxx.swiftpoet.TypeName import kotlinx.metadata.KmTypeProjection internal fun List.generateTupleType(moduleName: String): TupleTypeName = - TupleTypeName.of( - *this - .map { projection -> - (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) - .let { - if (projection.type?.isNullable == true && !it.optional) { - it.wrapOptional() - } else { - it - } + TupleTypeName( + this.map { projection -> + (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) + .let { + if (projection.type?.isNullable == true && !it.optional) { + it.wrapOptional() + } else { + it } - } - .map { "" to it } - .toTypedArray(), + } + } + .map { "" to it }, ) internal fun List.getTypes( diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt index f20ac06..2f0756a 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt @@ -78,7 +78,7 @@ internal fun TypeName.addGenericsAndOptional( }, isOuterSwift = isSwift, ) - this.parameterizedBy(*genericTypes.toTypedArray()) + this.parameterizedBy(genericTypes) } else { this }.let { From 2966b7c029a10cd8c09886812ef71a7cb578b7f7 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 5 Mar 2023 07:46:34 -0800 Subject: [PATCH 07/16] linting --- gradle/libs.versions.toml | 2 +- .../icerock/moko/kswift/plugin/KmClassExt.kt | 17 ++++++++++------- .../associatedenum/AssociatedEnumCase.kt | 3 +-- .../plugin/feature/associatedenum/OtherExt.kt | 3 ++- .../SealedToSwiftAssociatedEnumFeatureTest.kt | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f6724a8..078a22c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mokoResourcesVersion = "0.16.2" kotlinxMetadataKLibVersion = "0.0.1" kotlinPoetVersion = "1.6.0" -swiftPoetVersion = "1.5.1.1" +swiftPoetVersion = "1.5.1.2" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt index 53c6869..6308e0e 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmClassExt.kt @@ -14,7 +14,7 @@ import kotlinx.metadata.Flag import kotlinx.metadata.KmClass fun KmClass.buildTypeVariableNames( - kotlinFrameworkName: String + kotlinFrameworkName: String, ) = this.typeParameters.map { typeParam -> val bounds: List = typeParam.upperBounds .map { it.toTypeName(kotlinFrameworkName, isUsedInGenerics = true) } @@ -25,7 +25,7 @@ fun KmClass.buildTypeVariableNames( fun KmClass.getDeclaredTypeNameWithGenerics( kotlinFrameworkName: String, - classes: List + classes: List, ): TypeName { val typeVariables: List = buildTypeVariableNames(kotlinFrameworkName) val haveGenerics: Boolean = typeVariables.isNotEmpty() @@ -34,21 +34,24 @@ fun KmClass.getDeclaredTypeNameWithGenerics( @Suppress("SpreadOperator") return getDeclaredTypeName(kotlinFrameworkName, classes) .let { type -> - if (haveGenerics.not() || isInterface) type - else type.parameterizedBy(typeVariables) + if (haveGenerics.not() || isInterface) { + type + } else { + type.parameterizedBy(typeVariables) + } } } fun KmClass.getDeclaredTypeName( kotlinFrameworkName: String, - classes: List + classes: List, ): DeclaredTypeName { return DeclaredTypeName( moduleName = kotlinFrameworkName, simpleName = getSimpleName( className = name, - classes = classes - ) + classes = classes, + ), ) } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index 28c9e01..2f934f1 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -23,7 +23,7 @@ data class AssociatedEnumCase( private val explodedParams: List> = constructorParams.map { Pair( it.name, - it.type?.kotlinPrimitiveTypeNameToSwift(frameworkName) + it.type?.kotlinTypeToSwiftTypeName(frameworkName) ?: DeclaredTypeName.typeName("Swift.FailedToGetReturnType"), ) } @@ -49,7 +49,6 @@ data class AssociatedEnumCase( val unwrapped = paramType.unwrapOptional() when ((unwrapped as? ParameterizedTypeName)?.rawType) { DICTIONARY -> { - // TODO: unwrapped for other classes? unwrapped.toDictionaryCaster(it.first, true) } SET -> paramType.toSetCaster(it.first, true) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index f9f9b23..cbed1b5 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -46,6 +46,7 @@ internal fun KmType.kotlinTypeNameToInner( ) } +@Suppress("CyclomaticComplexMethod") private fun String.kotlinPrimitiveTypeNameToSwift( moduleName: String, arguments: List, @@ -79,7 +80,7 @@ private fun String.kotlinPrimitiveTypeNameToSwift( } } -internal fun KmType.kotlinPrimitiveTypeNameToSwift(moduleName: String): TypeName? { +internal fun KmType.kotlinTypeToSwiftTypeName(moduleName: String): TypeName? { val typeName = this.nameAsString return when { diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index 21e2d63..b04a169 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -31,7 +31,7 @@ class SealedToSwiftAssociatedEnumFeatureTest { ).doProcess( featureContext = it, fileSpecBuilder = fileSpecBuilder, - kotlinFrameworkName = "shared" + kotlinFrameworkName = "shared", ) } } From a8fbd2e5c227d9eea09599e918dce9dc1a9ab00c Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 5 Mar 2023 08:07:25 -0800 Subject: [PATCH 08/16] ktlint --- .editorconfig | 33 +++++++++++ .../icerock/moko/kswift/plugin/KmTypeExt.kt | 37 ++++++++----- .../SealedToSwiftAssociatedEnumFeature.kt | 11 +++- .../associatedenum/AssociatedEnumCase.kt | 10 ++-- .../AssociatedEnumCaseBuilder.kt | 2 +- .../ParameterizedTypeNameExt.kt | 55 +++++++++---------- .../feature/associatedenum/TypeSpecBuilder.kt | 40 ++++++-------- 7 files changed, 115 insertions(+), 73 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bbed6b0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,33 @@ +[*.{kt,kts}] +ktlint_code_style=android +ktlint_experimental_type-argument-list-spacing=enabled +ktlint_experimental_function-signature=enabled +ktlint_experimental_block-comment-initial-star-alignment=enabled +ktlint_experimental_unnecessary-parentheses-before-trailing-lambda=enabled +ktlint_experimental_class-naming=enabled +ktlint_experimental_package-naming=enabled +ktlint_experimental_fun-keyword-spacing=enabled +ktlint_experimental_function-return-type-spacing=enabled +ktlint_experimental_function-start-of-body-spacing=enabled +ktlint_experimental_function-type-reference-spacing=enabled +ktlint_experimental_modifier-list-spacing=enabled +ktlint_experimental_nullable-type-spacing=enabled +ktlint_experimental_parameter-list-spacing=enabled +ktlint_experimental_spacing-between-function-name-and-opening-parenthesis=enabled +ktlint_experimental_type-argument-list-spacing=enabled +ktlint_experimental_type-parameter-list-spacing=enabled +ktlint_experimental_comment-wrapping=enabled +ktlint_experimental_context-receiver-wrapping=enabled +ktlint_experimental_kdoc-wrapping=enabled + + +ktlint_ignore_back_ticked_identifier=true +ij_kotlin_allow_trailing_comma=true +ij_kotlin_allow_trailing_comma_on_call_site=true +indent_size = 4 +indent_style = space +insert_final_newline = true +# ktlint_function_signature_body_expression_wrapping = default +# ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = -1 +# ktlint_ignore_back_ticked_identifier = false +# max_line_length = -1 diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt index 0a9c268..d717b9b 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/KmTypeExt.kt @@ -25,15 +25,22 @@ fun KmType.toTypeName( moduleName: String, isUsedInGenerics: Boolean = false, typeVariables: Map = emptyMap(), - removeTypeVariables: Boolean = false + removeTypeVariables: Boolean = false, ): TypeName { return when (val classifier = classifier) { is KmClassifier.TypeParameter -> { val typeVariable: TypeVariableName? = typeVariables[classifier.id] if (typeVariable != null) { - return if (!removeTypeVariables) typeVariable - else typeVariable.bounds.firstOrNull()?.type ?: ANY_OBJECT - } else throw IllegalArgumentException("can't read type parameter $this without type variables list") + return if (!removeTypeVariables) { + typeVariable + } else { + typeVariable.bounds.firstOrNull()?.type ?: ANY_OBJECT + } + } else { + throw IllegalArgumentException( + "can't read type parameter $this without type variables list", + ) + } } is KmClassifier.TypeAlias -> { classifier.name.kotlinTypeNameToSwift(moduleName, isUsedInGenerics) @@ -46,7 +53,7 @@ fun KmType.toTypeName( moduleName, classifier.name, typeVariables, - removeTypeVariables + removeTypeVariables, ) } } @@ -76,16 +83,18 @@ fun String.kotlinTypeNameToSwift(moduleName: String, isUsedInGenerics: Boolean): val className: String = moduleAndClass[1] DeclaredTypeName.typeName( - listOf(module, className).joinToString(".") + listOf(module, className).joinToString("."), ).objcNameToSwift() } else if (this.startsWith("kotlin/Function")) { null } else if (this.startsWith("kotlin/") && this.count { it == '/' } == 1) { DeclaredTypeName( moduleName = moduleName, - simpleName = "Kotlin" + this.split("/").last() + simpleName = "Kotlin" + this.split("/").last(), ) - } else null + } else { + null + } } } } @@ -94,11 +103,11 @@ fun KmType.kotlinTypeToTypeName( moduleName: String, classifierName: ClassName, typeVariables: Map, - removeTypeVariables: Boolean + removeTypeVariables: Boolean, ): TypeName { val typeName = DeclaredTypeName( moduleName = moduleName, - simpleName = classifierName.split("/").last() + simpleName = classifierName.split("/").last(), ) if (this.arguments.isEmpty()) return typeName @@ -109,17 +118,17 @@ fun KmType.kotlinTypeToTypeName( moduleName = moduleName, isUsedInGenerics = false, typeVariables = typeVariables, - removeTypeVariables = removeTypeVariables + removeTypeVariables = removeTypeVariables, )!! val outputType: TypeName = arguments[1].type?.toTypeName( moduleName = moduleName, isUsedInGenerics = false, typeVariables = typeVariables, - removeTypeVariables = removeTypeVariables + removeTypeVariables = removeTypeVariables, )!! FunctionTypeName.get( parameters = listOf(ParameterSpec.unnamed(inputType)), - returnType = outputType + returnType = outputType, ) } else -> { @@ -128,7 +137,7 @@ fun KmType.kotlinTypeToTypeName( moduleName = moduleName, isUsedInGenerics = true, typeVariables = typeVariables, - removeTypeVariables = removeTypeVariables + removeTypeVariables = removeTypeVariables, ) } @Suppress("SpreadOperator") diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index c971559..5a4b12e 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -9,11 +9,11 @@ import dev.icerock.moko.kswift.plugin.feature.associatedenum.buildTypeSpec import dev.icerock.moko.kswift.plugin.getSimpleName import io.outfoxx.swiftpoet.FileSpec import io.outfoxx.swiftpoet.TypeSpec +import kotlin.reflect.KClass import kotlinx.metadata.Flag import kotlinx.metadata.KmClass import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging -import kotlin.reflect.KClass class SealedToSwiftAssociatedEnumFeature( override val featureContext: KClass, @@ -44,12 +44,17 @@ class SealedToSwiftAssociatedEnumFeature( if (featureContext.clazz.sealedSubclasses.isEmpty()) return - val sealedCases: List = buildEnumCases(kotlinFrameworkName, featureContext) + val sealedCases: List = buildEnumCases( + kotlinFrameworkName = kotlinFrameworkName, + featureContext = featureContext, + ) if (sealedCases.isEmpty()) { logger.warn("No public subclasses found for sealed class $originalClassName") return } else { - logger.lifecycle("Generating enum for sealed class $originalClassName (${sealedCases.size} public subclasses)") + logger.lifecycle( + "Generating enum for sealed class $originalClassName (${sealedCases.size} public subclasses)", + ) } val enumType: TypeSpec = buildTypeSpec( diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index 2f934f1..f85e2b5 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -38,10 +38,12 @@ data class AssociatedEnumCase( val paramType = (it.second as? ParameterizedTypeName) when { tupleType != null -> tupleType.generateTuple(it.first) - it.second.isCharacter -> "${it.first}: Character(UnicodeScalar(obj.${it.first})!)" - paramType?.rawType == DICTIONARY -> paramType.toDictionaryCaster( - it.first, - ) + it.second.isCharacter -> { + "${it.first}: Character(UnicodeScalar(obj.${it.first})!)" + } + paramType?.rawType == DICTIONARY -> { + paramType.toDictionaryCaster(it.first) + } paramType?.rawType == SET -> paramType.toSetCaster(it.first) paramType?.rawType == ARRAY -> paramType.toArrayCaster(it.first) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt index d00c745..3963b70 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt @@ -3,10 +3,10 @@ package dev.icerock.moko.kswift.plugin.feature.associatedenum import dev.icerock.moko.kswift.plugin.context.ClassContext import dev.icerock.moko.kswift.plugin.context.kLibClasses import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics +import java.util.Locale import kotlinx.metadata.ClassName import kotlinx.metadata.Flag import kotlinx.metadata.KmClass -import java.util.Locale fun buildEnumCases( kotlinFrameworkName: String, diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt index 252afa0..3a571ba 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt @@ -6,43 +6,40 @@ import io.outfoxx.swiftpoet.TypeName internal fun ParameterizedTypeName.toArrayCaster( paramName: String, optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! [") - .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) - .plus("]") - .plus(if (optional) " : nil" else "") +): String = "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus("]") + .plus(if (optional) " : nil" else "") internal fun ParameterizedTypeName.toSetCaster( paramName: String, optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! Set<") - .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) - .plus(">") - .plus(if (optional) " : nil" else "") +): String = "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! Set<") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus(">") + .plus(if (optional) " : nil" else "") internal fun ParameterizedTypeName.toDictionaryCaster( paramName: String, optional: Boolean = false, -): String = - "$paramName: " - .plus( - if (optional) "obj.$paramName != nil ? " else "", - ) - .plus("obj.$paramName as! [") - .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) - .plus(" : ") - .plus(this.typeArguments[1].kotlinInteropTypeWithFallback) - .plus("]") - .plus(if (optional) " : nil" else "") +): String = "$paramName: " + .plus( + if (optional) "obj.$paramName != nil ? " else "", + ) + .plus("obj.$paramName as! [") + .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus(" : ") + .plus(this.typeArguments[1].kotlinInteropTypeWithFallback) + .plus("]") + .plus(if (optional) " : nil" else "") internal fun TypeName.generateInitParameter(paramName: String): String { return "$paramName: " diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt index 4a1d391..f8219cb 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt @@ -88,9 +88,7 @@ private fun buildEnumConstructor( } add("} else {\n") indent() - add( - "fatalError(\"$className not synchronized with $originalClassName class\")\n", - ) + add("fatalError(\"$className not synchronized with $originalClassName class\")\n") unindent() add("}\n") } @@ -118,27 +116,25 @@ private fun buildSealedProperty( ).build() } -private fun buildSealedPropertyBody( - sealedCases: List, -): CodeBlock = CodeBlock.builder().apply { - add("switch self {\n") - sealedCases.forEach { enumCase -> - buildString { - append("case .") - append(enumCase.name) - append(enumCase.caseBlock) - append(":\n") - }.also { add(it) } - indent() - addSealedCaseReturnCode(enumCase) - unindent() +private fun buildSealedPropertyBody(sealedCases: List): CodeBlock = CodeBlock + .builder().apply { + add("switch self {\n") + sealedCases.forEach { enumCase -> + buildString { + append("case .") + append(enumCase.name) + append(enumCase.caseBlock) + append(":\n") + }.also { add(it) } + indent() + addSealedCaseReturnCode(enumCase) + unindent() + } + add("}\n") } - add("}\n") -}.build() + .build() -private fun CodeBlock.Builder.addSealedCaseReturnCode( - enumCase: AssociatedEnumCase, -) { +private fun CodeBlock.Builder.addSealedCaseReturnCode(enumCase: AssociatedEnumCase) { val parameters = enumCase.swiftToKotlinConstructor add("return ${enumCase.name.capitalized()}($parameters)\n") } From 98b7b4ed954a02de9821c77cdf7d5c321e613a10 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 5 Mar 2023 08:40:06 -0800 Subject: [PATCH 09/16] remove circle webhook --- .../plugin/SealedToSwiftAssociatedEnumFeatureTest.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index b04a169..dc1ef77 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -5,12 +5,13 @@ import dev.icerock.moko.kswift.plugin.context.LibraryContext import dev.icerock.moko.kswift.plugin.feature.Filter import dev.icerock.moko.kswift.plugin.feature.SealedToSwiftAssociatedEnumFeature import io.outfoxx.swiftpoet.FileSpec +import kotlin.test.Test +import kotlin.test.assertEquals import kotlinx.metadata.klib.KlibModuleMetadata import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy import org.jetbrains.kotlin.library.resolveSingleFileKlib -import kotlin.test.Test -import kotlin.test.assertEquals +@Suppress("LongMethod") class SealedToSwiftAssociatedEnumFeatureTest { @Test fun `associated enum feature should produce type mapped output`() { @@ -18,7 +19,11 @@ class SealedToSwiftAssociatedEnumFeatureTest { val klibPath = this::class.java.classLoader.getResource("associated-enum.klib") val konanFile = org.jetbrains.kotlin.konan.file.File(klibPath.toURI().path) // Need to use tooling strategy here since the klib was generated with 1.8 - val library = resolveSingleFileKlib(konanFile, strategy = ToolingSingleFileKlibResolveStrategy) + // kotlinc-native TestingSealed.kt -p library -o associated-enum + val library = resolveSingleFileKlib( + libraryFile = konanFile, + strategy = ToolingSingleFileKlibResolveStrategy, + ) val metadata = KlibModuleMetadata.read(KotlinMetadataLibraryProvider(library)) val libraryContext = LibraryContext(metadata) val fileSpecBuilder = FileSpec.builder(moduleName = "module", fileName = "file") From 3d0611c944bbfe7c697c0b83c1e4a4785ac256a2 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 6 Mar 2023 05:57:12 -0800 Subject: [PATCH 10/16] detekt --- .../SealedToSwiftAssociatedEnumFeature.kt | 9 +- .../associatedenum/AssociatedEnumCase.kt | 99 ++++++++++--------- .../AssociatedEnumCaseBuilder.kt | 2 +- .../plugin/feature/associatedenum/OtherExt.kt | 31 +++--- .../SealedToSwiftAssociatedEnumFeatureTest.kt | 6 +- 5 files changed, 81 insertions(+), 66 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt index 5a4b12e..ceec584 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/SealedToSwiftAssociatedEnumFeature.kt @@ -9,11 +9,11 @@ import dev.icerock.moko.kswift.plugin.feature.associatedenum.buildTypeSpec import dev.icerock.moko.kswift.plugin.getSimpleName import io.outfoxx.swiftpoet.FileSpec import io.outfoxx.swiftpoet.TypeSpec -import kotlin.reflect.KClass import kotlinx.metadata.Flag import kotlinx.metadata.KmClass import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging +import kotlin.reflect.KClass class SealedToSwiftAssociatedEnumFeature( override val featureContext: KClass, @@ -37,12 +37,11 @@ class SealedToSwiftAssociatedEnumFeature( kotlinFrameworkName: String, ) { val kmClass: KmClass = featureContext.clazz - - if (Flag.IS_PUBLIC(kmClass.flags).not()) return - val originalClassName: String = getSimpleName(kmClass.name, featureContext.kLibClasses) - if (featureContext.clazz.sealedSubclasses.isEmpty()) return + if (!Flag.IS_PUBLIC(kmClass.flags) || featureContext.clazz.sealedSubclasses.isEmpty()) { + return + } val sealedCases: List = buildEnumCases( kotlinFrameworkName = kotlinFrameworkName, diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index f85e2b5..7ce6c1c 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -11,6 +11,9 @@ import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.parameterizedBy import kotlinx.metadata.KmValueParameter +private const val PAIR = 2 +private const val TRIPLE = 3 + data class AssociatedEnumCase( val frameworkName: String, val name: String, @@ -101,53 +104,57 @@ data class AssociatedEnumCase( "$paramName: " + when { paramType.isCharacter -> "$paramName.utf16.first!" paramType is TupleTypeName -> { - if (paramType.types.size == 2) { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second + when (paramType.types.size) { + PAIR -> { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second - "KotlinPair<" - .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(")") - } else if (paramType.types.size == 3) { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second - val third = paramType.types[2] - val thirdType = third.second - "KotlinTriple<" - .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(thirdType.kotlinInteropTypeWithFallback.toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(", third: ") - .plus( - thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), - ) - .plus(")") - } else { - "unknown tuple type" + "KotlinPair<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(")") + } + TRIPLE -> { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + val third = paramType.types[2] + val thirdType = third.second + "KotlinTriple<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(thirdType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(", third: ") + .plus( + thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), + ) + .plus(")") + } + else -> { + "unknown tuple type" + } } } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt index 3963b70..d00c745 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt @@ -3,10 +3,10 @@ package dev.icerock.moko.kswift.plugin.feature.associatedenum import dev.icerock.moko.kswift.plugin.context.ClassContext import dev.icerock.moko.kswift.plugin.context.kLibClasses import dev.icerock.moko.kswift.plugin.getDeclaredTypeNameWithGenerics -import java.util.Locale import kotlinx.metadata.ClassName import kotlinx.metadata.Flag import kotlinx.metadata.KmClass +import java.util.Locale fun buildEnumCases( kotlinFrameworkName: String, diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index cbed1b5..564f36e 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -26,17 +26,12 @@ internal fun KmType.kotlinTypeNameToInner( return when { typeName == null -> null typeName.startsWith("kotlin/") -> { - when (namingMode) { - NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) - NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) - NamingMode.KOTLIN_NO_STRING -> - typeName - .kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - .let { if (it == STRING) NSSTRING else it } - } + kotlinPrimitiveToTypeNameWithNamingMode( + namingMode = namingMode, + typeName = typeName, + moduleName = moduleName, + ) } - else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) }?.addGenericsAndOptional( kmType = this, @@ -46,7 +41,21 @@ internal fun KmType.kotlinTypeNameToInner( ) } -@Suppress("CyclomaticComplexMethod") +private fun KmType.kotlinPrimitiveToTypeNameWithNamingMode( + namingMode: NamingMode, + typeName: String, + moduleName: String, +) = when (namingMode) { + NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) + NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) + NamingMode.KOTLIN_NO_STRING -> + typeName + .kotlinPrimitiveTypeNameToKotlinInterop(moduleName) + .let { if (it == STRING) NSSTRING else it } +} + +@Suppress("CyclomaticComplexMethod", "ComplexMethod") private fun String.kotlinPrimitiveTypeNameToSwift( moduleName: String, arguments: List, diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index dc1ef77..efea801 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -5,13 +5,13 @@ import dev.icerock.moko.kswift.plugin.context.LibraryContext import dev.icerock.moko.kswift.plugin.feature.Filter import dev.icerock.moko.kswift.plugin.feature.SealedToSwiftAssociatedEnumFeature import io.outfoxx.swiftpoet.FileSpec -import kotlin.test.Test -import kotlin.test.assertEquals import kotlinx.metadata.klib.KlibModuleMetadata import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy import org.jetbrains.kotlin.library.resolveSingleFileKlib +import kotlin.test.Test +import kotlin.test.assertEquals -@Suppress("LongMethod") +@Suppress("LongMethod", "MaxLineLength") class SealedToSwiftAssociatedEnumFeatureTest { @Test fun `associated enum feature should produce type mapped output`() { From baa0bbc9e302b998efc7f24956820d6f937d67e9 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 12 Mar 2023 10:59:29 -0700 Subject: [PATCH 11/16] improved generation for type parameters --- .../associatedenum/AssociatedEnumCase.kt | 117 ++++++++-------- .../AssociatedEnumCaseBuilder.kt | 4 +- .../associatedenum/KmTypeProjectionExt.kt | 16 ++- .../plugin/feature/associatedenum/OtherExt.kt | 67 ++++++--- .../feature/associatedenum/TypeNameExt.kt | 3 + .../feature/associatedenum/TypeSpecBuilder.kt | 2 +- .../SealedToSwiftAssociatedEnumFeatureTest.kt | 127 +++++++++++------- .../src/test/resources/associated-enum.klib | Bin 34010 -> 36589 bytes .../library/associatedenum/TestingSealed.kt | 7 + 9 files changed, 216 insertions(+), 127 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index 7ce6c1c..87852bf 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -9,6 +9,7 @@ import io.outfoxx.swiftpoet.SET import io.outfoxx.swiftpoet.TupleTypeName import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.parameterizedBy +import kotlinx.metadata.KmTypeParameter import kotlinx.metadata.KmValueParameter private const val PAIR = 2 @@ -22,11 +23,12 @@ data class AssociatedEnumCase( val caseArg: TypeName, val isObject: Boolean, val constructorParams: List, + val typeParameters: List, ) { private val explodedParams: List> = constructorParams.map { Pair( it.name, - it.type?.kotlinTypeToSwiftTypeName(frameworkName) + it.type?.kotlinTypeToSwiftTypeName(frameworkName, typeParameters) ?: DeclaredTypeName.typeName("Swift.FailedToGetReturnType"), ) } @@ -100,67 +102,68 @@ data class AssociatedEnumCase( } } - internal val swiftToKotlinConstructor: String = explodedParams.joinToString { (paramName, paramType) -> - "$paramName: " + when { - paramType.isCharacter -> "$paramName.utf16.first!" - paramType is TupleTypeName -> { - when (paramType.types.size) { - PAIR -> { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second + internal val swiftToKotlinConstructor: String = explodedParams + .joinToString { (paramName, paramType) -> + "$paramName: " + when { + paramType.isCharacter -> "$paramName.utf16.first!" + paramType is TupleTypeName -> { + when (paramType.types.size) { + PAIR -> { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second - "KotlinPair<" - .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(")") - } - TRIPLE -> { - val first = paramType.types[0] - val firstType = first.second - val second = paramType.types[1] - val secondType = second.second - val third = paramType.types[2] - val thirdType = third.second - "KotlinTriple<" - .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) - .plus(", ") - .plus(thirdType.kotlinInteropTypeWithFallback.toNSString()) - .plus(">(first: ") - .plus( - firstType.generateKotlinConstructorIfNecessary("$paramName.0"), - ) - .plus(", second: ") - .plus( - secondType.generateKotlinConstructorIfNecessary("$paramName.1"), - ) - .plus(", third: ") - .plus( - thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), - ) - .plus(")") - } - else -> { - "unknown tuple type" + "KotlinPair<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(")") + } + TRIPLE -> { + val first = paramType.types[0] + val firstType = first.second + val second = paramType.types[1] + val secondType = second.second + val third = paramType.types[2] + val thirdType = third.second + "KotlinTriple<" + .plus(firstType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(secondType.kotlinInteropTypeWithFallback.toNSString()) + .plus(", ") + .plus(thirdType.kotlinInteropTypeWithFallback.toNSString()) + .plus(">(first: ") + .plus( + firstType.generateKotlinConstructorIfNecessary("$paramName.0"), + ) + .plus(", second: ") + .plus( + secondType.generateKotlinConstructorIfNecessary("$paramName.1"), + ) + .plus(", third: ") + .plus( + thirdType.generateKotlinConstructorIfNecessary("$paramName.2"), + ) + .plus(")") + } + else -> { + "unknown tuple type" + } } } - } - else -> paramType.generateKotlinConstructorIfNecessaryForParameter(paramName) + else -> paramType.generateKotlinConstructorIfNecessaryForParameter(paramName) + } } - } } private fun String.toNSString(): String = diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt index d00c745..a8ccd28 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCaseBuilder.kt @@ -40,7 +40,8 @@ private fun buildEnumCase( } val decapitalizedName: String = name.decapitalize(Locale.ROOT) - val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) + val isObject: Boolean = Flag.Class.IS_OBJECT(sealedCaseClass.flags) || + sealedCaseClass.constructors.first().valueParameters.isEmpty() val caseArg = sealedCaseClass.getDeclaredTypeNameWithGenerics( kotlinFrameworkName = kotlinFrameworkName, classes = featureContext.kLibClasses, @@ -58,5 +59,6 @@ private fun buildEnumCase( caseArg = caseArg, isObject = isObject, constructorParams = sealedCaseClass.constructors.first().valueParameters, + typeParameters = sealedCaseClass.typeParameters, ) } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt index 6522c06..1e565d3 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/KmTypeProjectionExt.kt @@ -4,12 +4,21 @@ import dev.icerock.moko.kswift.plugin.isNullable import io.outfoxx.swiftpoet.ANY_OBJECT import io.outfoxx.swiftpoet.TupleTypeName import io.outfoxx.swiftpoet.TypeName +import kotlinx.metadata.KmTypeParameter import kotlinx.metadata.KmTypeProjection -internal fun List.generateTupleType(moduleName: String): TupleTypeName = +internal fun List.generateTupleType( + moduleName: String, + typeParameters: List, +): TupleTypeName = TupleTypeName( this.map { projection -> - (projection.type?.kotlinTypeNameToInner(moduleName, NamingMode.SWIFT, true) ?: ANY_OBJECT) + (projection.type?.kotlinTypeNameToInner( + moduleName = moduleName, + namingMode = NamingMode.SWIFT, + isOuterSwift = true, + typeParameters = typeParameters, + ) ?: ANY_OBJECT) .let { if (projection.type?.isNullable == true && !it.optional) { it.wrapOptional() @@ -25,6 +34,7 @@ internal fun List.getTypes( moduleName: String, namingMode: NamingMode, isOuterSwift: Boolean, + typeParameters: List, ): List = this.map { - it.type?.kotlinTypeNameToInner(moduleName, namingMode, isOuterSwift) ?: ANY_OBJECT + it.type?.kotlinTypeNameToInner(moduleName, namingMode, isOuterSwift, typeParameters) ?: ANY_OBJECT } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index 564f36e..77355e0 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -1,5 +1,6 @@ package dev.icerock.moko.kswift.plugin.feature.associatedenum +import dev.icerock.moko.kswift.plugin.isNullable import dev.icerock.moko.kswift.plugin.objcNameToSwift import io.outfoxx.swiftpoet.ANY_OBJECT import io.outfoxx.swiftpoet.ARRAY @@ -11,8 +12,10 @@ import io.outfoxx.swiftpoet.SET import io.outfoxx.swiftpoet.STRING import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.VOID +import kotlinx.metadata.Flag import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmType +import kotlinx.metadata.KmTypeParameter import kotlinx.metadata.KmTypeProjection private val NSSTRING = DeclaredTypeName(moduleName = "Foundation", simpleName = "NSString") @@ -21,8 +24,9 @@ internal fun KmType.kotlinTypeNameToInner( moduleName: String, namingMode: NamingMode, isOuterSwift: Boolean, + typeParameters: List, ): TypeName? { - val typeName = this.nameAsString + val typeName = this.nameAsString(typeParameters) return when { typeName == null -> null typeName.startsWith("kotlin/") -> { @@ -30,6 +34,7 @@ internal fun KmType.kotlinTypeNameToInner( namingMode = namingMode, typeName = typeName, moduleName = moduleName, + typeParameters = typeParameters, ) } else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) @@ -38,6 +43,7 @@ internal fun KmType.kotlinTypeNameToInner( moduleName = moduleName, namingMode = namingMode, isOuterSwift = isOuterSwift, + typeParameters = typeParameters, ) } @@ -45,9 +51,10 @@ private fun KmType.kotlinPrimitiveToTypeNameWithNamingMode( namingMode: NamingMode, typeName: String, moduleName: String, + typeParameters: List, ) = when (namingMode) { NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments) + NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments, typeParameters) NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) NamingMode.KOTLIN_NO_STRING -> typeName @@ -55,19 +62,19 @@ private fun KmType.kotlinPrimitiveToTypeNameWithNamingMode( .let { if (it == STRING) NSSTRING else it } } -@Suppress("CyclomaticComplexMethod", "ComplexMethod") private fun String.kotlinPrimitiveTypeNameToSwift( moduleName: String, arguments: List, + typeParameters: List, ): TypeName { require(this.startsWith("kotlin/")) return when (this) { "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") - "kotlin/Pair" -> arguments.generateTupleType(moduleName) + "kotlin/Pair" -> arguments.generateTupleType(moduleName, typeParameters) "kotlin/Result" -> ANY_OBJECT "kotlin/String" -> STRING - "kotlin/Triple" -> arguments.generateTupleType(moduleName) + "kotlin/Triple" -> arguments.generateTupleType(moduleName, typeParameters) "kotlin/Throwable" -> DeclaredTypeName( moduleName = moduleName, simpleName = "KotlinThrowable", @@ -77,25 +84,32 @@ private fun String.kotlinPrimitiveTypeNameToSwift( "kotlin/collections/List" -> ARRAY "kotlin/collections/Map" -> DICTIONARY "kotlin/collections/Set" -> SET - else -> { - if (this.startsWith("kotlin/Function")) { - val typedArgs = arguments.getTypes(moduleName, NamingMode.KOTLIN, false) - val types = typedArgs.map { ParameterSpec.unnamed(it) }.dropLast(1) - FunctionTypeName.get(types, typedArgs.last()) - } else { - kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) - } - } + else -> unknownKotlinPrimitiveTypeToSwift(arguments, moduleName, typeParameters) } } -internal fun KmType.kotlinTypeToSwiftTypeName(moduleName: String): TypeName? { - val typeName = this.nameAsString +private fun String.unknownKotlinPrimitiveTypeToSwift( + arguments: List, + moduleName: String, + typeParameters: List, +) = if (this.startsWith("kotlin/Function")) { + val typedArgs = arguments.getTypes(moduleName, NamingMode.KOTLIN, false, typeParameters) + val types = typedArgs.map { ParameterSpec.unnamed(it) }.dropLast(1) + FunctionTypeName.get(types, typedArgs.last()) +} else { + kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) +} + +internal fun KmType.kotlinTypeToSwiftTypeName( + moduleName: String, + typeParameters: List, +): TypeName? { + val typeName = this.nameAsString(typeParameters) return when { typeName == null -> null typeName.startsWith("kotlin/") -> - typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments) + typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments, typeParameters) else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) }?.addGenericsAndOptional( @@ -103,16 +117,29 @@ internal fun KmType.kotlinTypeToSwiftTypeName(moduleName: String): TypeName? { moduleName = moduleName, namingMode = null, isOuterSwift = true, + typeParameters = typeParameters, ) } -private val KmType.nameAsString: String? - get() = when (val classifier = this.classifier) { +private fun KmType.nameAsString(typeParameters: List): String? = + when (val classifier = this.classifier) { is KmClassifier.Class -> classifier.name - is KmClassifier.TypeParameter -> null + is KmClassifier.TypeParameter -> { + typeParameters.recursivelyResolveToName(classifier.id) + } is KmClassifier.TypeAlias -> classifier.name } +private fun List.recursivelyResolveToName(id: Int): String? { + return try { + this[id].name + if (this[id].upperBounds.firstOrNull()?.isNullable != false) "?" else "" + } catch (e: IndexOutOfBoundsException) { + (id - this.size).takeIf { it >= 0 }?.let { indexInParent -> + this.recursivelyResolveToName(indexInParent) + } + } +} + private fun String.kotlinPrimitiveTypeNameToKotlinInterop(moduleName: String): TypeName { require(this.startsWith("kotlin/")) return when (this) { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt index 2f0756a..7f73ffb 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt @@ -11,6 +11,7 @@ import io.outfoxx.swiftpoet.STRING import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.parameterizedBy import kotlinx.metadata.KmType +import kotlinx.metadata.KmTypeParameter internal val TypeName.isCharacter: Boolean get() = this.name == "Swift.Character" @@ -63,6 +64,7 @@ internal fun TypeName.addGenericsAndOptional( moduleName: String, namingMode: NamingMode?, isOuterSwift: Boolean, + typeParameters: List, ): TypeName { val isSwift = (this as? DeclaredTypeName)?.moduleName == "Swift" @@ -77,6 +79,7 @@ internal fun TypeName.addGenericsAndOptional( else -> NamingMode.OBJC }, isOuterSwift = isSwift, + typeParameters = typeParameters, ) this.parameterizedBy(genericTypes) } else { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt index f8219cb..ea600c8 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeSpecBuilder.kt @@ -136,5 +136,5 @@ private fun buildSealedPropertyBody(sealedCases: List): Code private fun CodeBlock.Builder.addSealedCaseReturnCode(enumCase: AssociatedEnumCase) { val parameters = enumCase.swiftToKotlinConstructor - add("return ${enumCase.name.capitalized()}($parameters)\n") + add("return ${enumCase.caseArg}($parameters)\n") } diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index efea801..abdd198 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -15,7 +15,6 @@ import kotlin.test.assertEquals class SealedToSwiftAssociatedEnumFeatureTest { @Test fun `associated enum feature should produce type mapped output`() { - // Generated from TestSealed.kt val klibPath = this::class.java.classLoader.getResource("associated-enum.klib") val konanFile = org.jetbrains.kotlin.konan.file.File(klibPath.toURI().path) // Need to use tooling strategy here since the klib was generated with 1.8 @@ -47,7 +46,45 @@ class SealedToSwiftAssociatedEnumFeatureTest { import shared /** - * selector: ClassContext/associatedenum/com/icerockdev/library/associatedenum/TestingSealed */ + * selector: ClassContext/associated-enum/com/icerockdev/library/associatedenum/LoadingState */ +public enum LoadingStateKs { + + case errorOnLoad(error: String) + case loading + case other(otherPayload: P) + case success(payload: T?) + + public var sealed: LoadingState { + switch self { + case .errorOnLoad(let error): + return shared.LoadingStateErrorOnLoad(error: error) + case .loading: + return shared.LoadingStateLoading() + case .other(let otherPayload): + return shared.LoadingStateOther(otherPayload: otherPayload) + case .success(let payload): + return shared.LoadingStateSuccess(payload: payload) + } + } + + public init(_ obj: LoadingState) { + if let obj = obj as? shared.LoadingStateErrorOnLoad { + self = .errorOnLoad(error: obj.error as String) + } else if obj is shared.LoadingStateLoading { + self = .loading + } else if let obj = obj as? shared.LoadingStateOther { + self = .other(otherPayload: obj.otherPayload) + } else if let obj = obj as? shared.LoadingStateSuccess { + self = .success(payload: obj.payload) + } else { + fatalError("LoadingStateKs not synchronized with LoadingState class") + } + } + +} + +/** + * selector: ClassContext/associated-enum/com/icerockdev/library/associatedenum/TestingSealed */ public enum TestingSealedKs { case hasChar(mychar: Character) @@ -101,91 +138,91 @@ public enum TestingSealedKs { public var sealed: TestingSealed { switch self { case .hasChar(let mychar): - return HasChar(mychar: mychar.utf16.first!) + return shared.HasChar(mychar: mychar.utf16.first!) case .hasEnum(let myenum): - return HasEnum(myenum: myenum) + return shared.HasEnum(myenum: myenum) case .hasFunction(let myfunc): - return HasFunction(myfunc: myfunc) + return shared.HasFunction(myfunc: myfunc) case .hasInnerList(let innerList): - return HasInnerList(innerList: innerList) + return shared.HasInnerList(innerList: innerList) case .hasInnerNullable(let innerList): - return HasInnerNullable(innerList: innerList) + return shared.HasInnerNullable(innerList: innerList) case .hasListInt(let hasGeneric): - return HasListInt(hasGeneric: hasGeneric) + return shared.HasListInt(hasGeneric: hasGeneric) case .hasListIntNullable(let hasGeneric): - return HasListIntNullable(hasGeneric: hasGeneric) + return shared.HasListIntNullable(hasGeneric: hasGeneric) case .hasListOwn(let hasGeneric): - return HasListOwn(hasGeneric: hasGeneric) + return shared.HasListOwn(hasGeneric: hasGeneric) case .hasListString(let hasGeneric): - return HasListString(hasGeneric: hasGeneric) + return shared.HasListString(hasGeneric: hasGeneric) case .hasListStringNullable(let hasGeneric): - return HasListStringNullable(hasGeneric: hasGeneric) + return shared.HasListStringNullable(hasGeneric: hasGeneric) case .hasListStringOuterNullable(let hasGeneric): - return HasListStringOuterNullable(hasGeneric: hasGeneric != nil ? hasGeneric : nil) + return shared.HasListStringOuterNullable(hasGeneric: hasGeneric != nil ? hasGeneric : nil) case .hasMap(let map): - return HasMap(map: map) + return shared.HasMap(map: map) case .hasMapNullableOuter(let map): - return HasMapNullableOuter(map: map != nil ? map : nil) + return shared.HasMapNullableOuter(map: map != nil ? map : nil) case .hasMapNullableParams(let map): - return HasMapNullableParams(map: map) + return shared.HasMapNullableParams(map: map) case .hasMultipleOwnParams(let p1, let p2): - return HasMultipleOwnParams(p1: p1, p2: p2 != nil ? p2 : nil) + return shared.HasMultipleOwnParams(p1: p1, p2: p2 != nil ? p2 : nil) case .hasNestedGeneric(let nested): - return HasNestedGeneric(nested: nested) + return shared.HasNestedGeneric(nested: nested) case .hasNullableInnerList(let innerList): - return HasNullableInnerList(innerList: innerList) + return shared.HasNullableInnerList(innerList: innerList) case .hasNullableOuterList(let innerList): - return HasNullableOuterList(innerList: innerList != nil ? innerList : nil) + return shared.HasNullableOuterList(innerList: innerList != nil ? innerList : nil) case .hasOtherNullables(let mystring, let optstring, let myfloat, let optfloat, let mydouble, let optdouble): - return HasOtherNullables(mystring: mystring, optstring: optstring != nil ? optstring : nil, myfloat: myfloat, optfloat: optfloat != nil ? KotlinFloat(value: optfloat!) : nil, mydouble: mydouble, optdouble: optdouble != nil ? KotlinDouble(value: optdouble!) : nil) + return shared.HasOtherNullables(mystring: mystring, optstring: optstring != nil ? optstring : nil, myfloat: myfloat, optfloat: optfloat != nil ? KotlinFloat(value: optfloat!) : nil, mydouble: mydouble, optdouble: optdouble != nil ? KotlinDouble(value: optdouble!) : nil) case .hasOwnClass(let ownClass): - return HasOwnClass(ownClass: ownClass) + return shared.HasOwnClass(ownClass: ownClass) case .hasOwnClassWithGeneric(let ownClassWithGeneric): - return HasOwnClassWithGeneric(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGeneric(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericAny(let ownClassWithGeneric): - return HasOwnClassWithGenericAny(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericAny(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericEnum(let ownClassWithGeneric): - return HasOwnClassWithGenericEnum(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericEnum(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericInnerMap(let ownClassWithGeneric): - return HasOwnClassWithGenericInnerMap(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericInnerMap(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericInnerPair(let ownClassWithGeneric): - return HasOwnClassWithGenericInnerPair(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericInnerPair(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericInnerSet(let ownClassWithGeneric): - return HasOwnClassWithGenericInnerSet(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericInnerSet(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericNested(let ownClassWithGeneric): - return HasOwnClassWithGenericNested(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericNested(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericNullable(let ownClassWithGeneric): - return HasOwnClassWithGenericNullable(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericNullable(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericThrowable(let ownClassWithGeneric): - return HasOwnClassWithGenericThrowable(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericThrowable(ownClassWithGeneric: ownClassWithGeneric) case .hasOwnClassWithGenericWildcard(let ownClassWithGeneric): - return HasOwnClassWithGenericWildcard(ownClassWithGeneric: ownClassWithGeneric) + return shared.HasOwnClassWithGenericWildcard(ownClassWithGeneric: ownClassWithGeneric) case .hasPairBool(let pair): - return HasPairBool(pair: KotlinPair(first: KotlinBoolean(value: pair.0), second: pair.1 != nil ? KotlinBoolean(value: pair.1!) : nil)) + return shared.HasPairBool(pair: KotlinPair(first: KotlinBoolean(value: pair.0), second: pair.1 != nil ? KotlinBoolean(value: pair.1!) : nil)) case .hasPairFloat(let pair): - return HasPairFloat(pair: KotlinPair(first: KotlinFloat(value: pair.0), second: pair.1 != nil ? KotlinFloat(value: pair.1!) : nil)) + return shared.HasPairFloat(pair: KotlinPair(first: KotlinFloat(value: pair.0), second: pair.1 != nil ? KotlinFloat(value: pair.1!) : nil)) case .hasPairGeneric(let pair): - return HasPairGeneric(pair: KotlinPair(first: KotlinUByte(value: pair.0), second: pair.1 != nil ? pair.1 : nil)) + return shared.HasPairGeneric(pair: KotlinPair(first: KotlinUByte(value: pair.0), second: pair.1 != nil ? pair.1 : nil)) case .hasPairString(let pair): - return HasPairString(pair: KotlinPair(first: pair.0 as NSString, second: pair.1 != nil ? pair.1! as NSString : nil)) + return shared.HasPairString(pair: KotlinPair(first: pair.0 as NSString, second: pair.1 != nil ? pair.1! as NSString : nil)) case .hasSet(let myset): - return HasSet(myset: myset) + return shared.HasSet(myset: myset) case .hasSetNullableInt(let myset): - return HasSetNullableInt(myset: myset) + return shared.HasSetNullableInt(myset: myset) case .hasSetNullableOuter(let myset): - return HasSetNullableOuter(myset: myset != nil ? myset : nil) + return shared.HasSetNullableOuter(myset: myset != nil ? myset : nil) case .hasSetString(let myset): - return HasSetString(myset: myset) + return shared.HasSetString(myset: myset) case .hasSetStringNullable(let myset): - return HasSetStringNullable(myset: myset) + return shared.HasSetStringNullable(myset: myset) case .hasSomeNullables(let myint, let myintopt, let uintnotoptional, let uintoptional, let mybool, let optbool): - return HasSomeNullables(myint: myint, myintopt: myintopt != nil ? KotlinInt(value: myintopt!) : nil, uintnotoptional: uintnotoptional, uintoptional: uintoptional != nil ? KotlinUInt(value: uintoptional!) : nil, mybool: mybool, optbool: optbool != nil ? KotlinBoolean(value: optbool!) : nil) + return shared.HasSomeNullables(myint: myint, myintopt: myintopt != nil ? KotlinInt(value: myintopt!) : nil, uintnotoptional: uintnotoptional, uintoptional: uintoptional != nil ? KotlinUInt(value: uintoptional!) : nil, mybool: mybool, optbool: optbool != nil ? KotlinBoolean(value: optbool!) : nil) case .hasThrowable(let throwable): - return HasThrowable(throwable: throwable) + return shared.HasThrowable(throwable: throwable) case .hasTriple(let triple): - return HasTriple(triple: KotlinTriple(first: KotlinFloat(value: triple.0), second: triple.1 != nil ? KotlinInt(value: triple.1!) : nil, third: triple.2)) + return shared.HasTriple(triple: KotlinTriple(first: KotlinFloat(value: triple.0), second: triple.1 != nil ? KotlinInt(value: triple.1!) : nil, third: triple.2)) case .justAnObj: - return JustAnObj() + return shared.JustAnObj() } } diff --git a/kswift-gradle-plugin/src/test/resources/associated-enum.klib b/kswift-gradle-plugin/src/test/resources/associated-enum.klib index cc4c87265db0710f3affb325c83a9b283ac45495..48b521d44de2c66f858da2c14673ba83ef6a2cd4 100644 GIT binary patch literal 36589 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 Ie)H=80a+|Y+5i9m literal 34010 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

YRIKo>=B5BfqwFK4<(9Zb~``B@tv^$4+AF4`$D7 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� zN98H7umf|t>UxAboP<Q(IP0_@cMc3~ZR0)QC0aZVZ1jpq)8}M4lw21+|dV zh^$`75;kf6N((OI45whJOOvAkmq(GTZ5j)Jp<3c0rO$xtB*vWgXr?j9#$C|*Dmb8XlGGpOrA zNPQ1+a|X)AK`ty!&n&;gp+0yNDAG9FYAw>LZI?0$B0h;0-b>~j1bF34$ql(T$G?+d zjwX??xXzzZ9@i#pfu1d5VV4pJBKB=bz|t7HoB2GPLODb=*pES6(B=7-6%);ENg?#7 zfl{;<0fOP-he60gPr{4oK814gG!^(9%2}RICB#YZ-+zy9{Jz2AeLAsOBtwVDJaCK# zwwld!wsaEg{HV(nZKRwX=8FZ1ENZ$}ztB9w7$d8~Zuy+%Xz+87t~w z4%n2~Hqd!KpIuNtxBE3HyVHF_i?0?Idt0xEiWT83J@h(mlr^dpS8&ZeS9pkpp?iDwk{6VeUFm9#zsbZn}#PZy}dy%R!2g>g5+PQ9dG zrD@K2gki`J5mp|cvvnF_bTAX2z7BfA)5*JX`yqV!8==fwNFne$o)ttjlg4p~!LKL9 zD~WK&t1ZRrB;I>vuTuI(a?Y$m{yZ&9dwBkX14!Y6gO?S@+)Lw3t3%BSSrJE8KP6r} z^sCa99KDrt&1h%QTBRVv%pN`#)V4Wo8}g#p<}pv&ev#fJdg4D*J2#WKzayTVPBVwE zUUC3Q3ETcONHR5R(ln$|V-eIkEG}bl>!lh_#LbO^XtDlb=FXoQa#Ic;#*E-8=rTCb z-bzVWX@jDBmE9O3XFuC-FPQ~sRD7>oG!ix)-l!0ei+I`*VELgZwc|nkI$EL(a}7VE zro-D_2I1~S!S0E6N-GM8V)bM&v(&FU?wpc zpFS<5kPVOD@UDB>8J@o?3K$uDnR~Dsuf1d4<_;LS-9Rc;+;gzA5J7g(u$-Ea7I z*H#=jnBQD%E=M3vJlnJmkKXgC! ziG1mWwBX~j>l^AatLitJOFsW_dSc_H*IyF6<^UPoJ6hK2FbtG^GH#|+^<6!n;9IF0 zr#*~4>s7g>z`d3b8-~KT4UEIPS}|(h6xlT4<*Au*7=_t>y;f}WAg6Cvwng1@YtfxlYt zQuY6f+WGJ3BmIAX2b%n|NdGVQ2`ao-ZhBM&ID~UKOLE9A)PFi6;*azj3iE~fsAza7 zzrK0EK|y->%FmzUwK4w~uYF(T_m`7gxm*+p^v}!x9JBpDz|dZSar*_#<#F5p10d5C zz=ppB{MXp+zl}?b?CJlzzJCh(ukqXe11`%Ic}@Nj_tF^d{{b2Q3UbLMQV%{rQ|Nf!H z-{4(-$ann;Ughm8c)xmO`!|@EANE`|-AmIen3oZ8@xGW{f%>;+MZbZ& z+~}ja0@sCn$obDeMtb7PqnJxzzatT{D_~wWe}Y}^6kYom9Uh$rmvAnNbGbi8eFdlES2+Ld*j+;Vom8Ax(5!5KA=R%l4>IFi zg1;>6Za1>3(=!^A?C S9Vm3jf3C { enum class OwnEnum { A, B, C } + +sealed class LoadingState { + class Loading : LoadingState() + data class Success(val payload: T) : LoadingState() + data class Other(val otherPayload: P) : LoadingState() + data class ErrorOnLoad(val error: String) : LoadingState() +} From 43b40001890b3b572abf3cd54ba2ab7f49362633 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sun, 5 Mar 2023 10:03:17 -0800 Subject: [PATCH 12/16] upgrade swiftpoet --- gradle/libs.versions.toml | 2 +- .../plugin/feature/associatedenum/SwiftTypeMappings.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 078a22c..c259f91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mokoResourcesVersion = "0.16.2" kotlinxMetadataKLibVersion = "0.0.1" kotlinPoetVersion = "1.6.0" -swiftPoetVersion = "1.5.1.2" +swiftPoetVersion = "1.5.1.3" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt index c4d9918..06d4613 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/SwiftTypeMappings.kt @@ -10,7 +10,7 @@ import io.outfoxx.swiftpoet.INT32 import io.outfoxx.swiftpoet.INT64 import io.outfoxx.swiftpoet.INT8 import io.outfoxx.swiftpoet.ParameterizedTypeName -import io.outfoxx.swiftpoet.UIN16 +import io.outfoxx.swiftpoet.UINT16 import io.outfoxx.swiftpoet.UINT32 import io.outfoxx.swiftpoet.UINT64 import io.outfoxx.swiftpoet.UINT8 @@ -27,7 +27,7 @@ internal val kotlinToSwiftTypeMap: Map = mapOf( "kotlin/UByte" to UINT8, "kotlin/UInt" to UINT32, "kotlin/ULong" to UINT64, - "kotlin/UShort" to UIN16, + "kotlin/UShort" to UINT16, ) internal val swiftTypeToKotlinMap: Map = mapOf( @@ -42,7 +42,7 @@ internal val swiftTypeToKotlinMap: Map = mapOf( UINT8 to "kotlin/UByte", UINT32 to "kotlin/UInt", UINT64 to "kotlin/ULong", - UIN16 to "kotlin/UShort", + UINT16 to "kotlin/UShort", ) internal val swiftOptionalTypeToKotlinMap: Map = From fca64d7fe1dffebea504ef587cdd97d43fbe5d68 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 13 Mar 2023 06:30:35 -0700 Subject: [PATCH 13/16] formatting for sonar --- .../plugin/feature/associatedenum/OtherExt.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index 77355e0..1c1394d 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -12,7 +12,6 @@ import io.outfoxx.swiftpoet.SET import io.outfoxx.swiftpoet.STRING import io.outfoxx.swiftpoet.TypeName import io.outfoxx.swiftpoet.VOID -import kotlinx.metadata.Flag import kotlinx.metadata.KmClassifier import kotlinx.metadata.KmType import kotlinx.metadata.KmTypeParameter @@ -54,7 +53,11 @@ private fun KmType.kotlinPrimitiveToTypeNameWithNamingMode( typeParameters: List, ) = when (namingMode) { NamingMode.KOTLIN -> typeName.kotlinPrimitiveTypeNameToKotlinInterop(moduleName) - NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, arguments, typeParameters) + NamingMode.SWIFT -> typeName.kotlinPrimitiveTypeNameToSwift( + moduleName = moduleName, + arguments = arguments, + typeParameters = typeParameters, + ) NamingMode.OBJC -> typeName.kotlinPrimitiveTypeNameToObjectiveC(moduleName) NamingMode.KOTLIN_NO_STRING -> typeName @@ -131,12 +134,16 @@ private fun KmType.nameAsString(typeParameters: List): String? } private fun List.recursivelyResolveToName(id: Int): String? { - return try { - this[id].name + if (this[id].upperBounds.firstOrNull()?.isNullable != false) "?" else "" - } catch (e: IndexOutOfBoundsException) { - (id - this.size).takeIf { it >= 0 }?.let { indexInParent -> - this.recursivelyResolveToName(indexInParent) + return when { + id >= this.size -> this.recursivelyResolveToName(id - this.size) + id >= 0 -> { + this[id].name + if (this[id].upperBounds.firstOrNull()?.isNullable != false) { + "?" + } else { + "" + } } + else -> null } } From cef8e9a2840b764936f09d6c2630bcbd952fe019 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 5 Jun 2023 11:12:01 -0700 Subject: [PATCH 14/16] add support for immutable collections --- gradle/libs.versions.toml | 2 +- kswift-gradle-plugin/build.gradle.kts | 1 + .../plugin/feature/associatedenum/OtherExt.kt | 19 ++++++++++----- .../SealedToSwiftAssociatedEnumFeatureTest.kt | 1 + .../plugin/associatedenum/TestInstances.kt | 15 ++++++++++++ .../plugin}/associatedenum/TestingSealed.kt | 24 ++++++++++++++----- .../library/associatedenum/TestInstances.kt | 19 --------------- 7 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestInstances.kt rename {sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library => kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin}/associatedenum/TestingSealed.kt (87%) delete mode 100644 sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestInstances.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c259f91..568c3c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mokoResourcesVersion = "0.16.2" kotlinxMetadataKLibVersion = "0.0.1" kotlinPoetVersion = "1.6.0" -swiftPoetVersion = "1.5.1.3" +swiftPoetVersion = "1.5.1.4" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } diff --git a/kswift-gradle-plugin/build.gradle.kts b/kswift-gradle-plugin/build.gradle.kts index c8b48aa..fdb6870 100644 --- a/kswift-gradle-plugin/build.gradle.kts +++ b/kswift-gradle-plugin/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { api(libs.kotlinxMetadataKLib) testImplementation(libs.kotlinTestJUnit) + testImplementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") } gradlePlugin { diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index 1c1394d..c8c5ef9 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -70,7 +70,8 @@ private fun String.kotlinPrimitiveTypeNameToSwift( arguments: List, typeParameters: List, ): TypeName { - require(this.startsWith("kotlin/")) + require(this.startsWith("kotlin/") || this.startsWith("kotlinx/collections/immutable/")) + println(this) return when (this) { "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") @@ -84,9 +85,15 @@ private fun String.kotlinPrimitiveTypeNameToSwift( ) "kotlin/Unit" -> VOID - "kotlin/collections/List" -> ARRAY - "kotlin/collections/Map" -> DICTIONARY - "kotlin/collections/Set" -> SET + "kotlinx/collections/immutable/ImmutableList", + "kotlin/collections/List", + -> ARRAY + "kotlinx/collections/immutable/ImmutableMap", + "kotlin/collections/Map", + -> DICTIONARY + "kotlinx/collections/immutable/ImmutableSet", + "kotlin/collections/Set", + -> SET else -> unknownKotlinPrimitiveTypeToSwift(arguments, moduleName, typeParameters) } } @@ -103,7 +110,7 @@ private fun String.unknownKotlinPrimitiveTypeToSwift( kotlinToSwiftTypeMap[this] ?: this.kotlinInteropName(moduleName) } -internal fun KmType.kotlinTypeToSwiftTypeName( +fun KmType.kotlinTypeToSwiftTypeName( moduleName: String, typeParameters: List, ): TypeName? { @@ -111,7 +118,7 @@ internal fun KmType.kotlinTypeToSwiftTypeName( return when { typeName == null -> null - typeName.startsWith("kotlin/") -> + typeName.startsWith("kotlin/") || typeName.startsWith("kotlinx/collections/immutable/") -> typeName.kotlinPrimitiveTypeNameToSwift(moduleName, this.arguments, typeParameters) else -> getDeclaredTypeNameFromNonPrimitive(typeName, moduleName) diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index abdd198..a46ec5f 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -19,6 +19,7 @@ class SealedToSwiftAssociatedEnumFeatureTest { val konanFile = org.jetbrains.kotlin.konan.file.File(klibPath.toURI().path) // Need to use tooling strategy here since the klib was generated with 1.8 // kotlinc-native TestingSealed.kt -p library -o associated-enum + // or from project ./gradlew iosArm64MainKlibrary val library = resolveSingleFileKlib( libraryFile = konanFile, strategy = ToolingSingleFileKlibResolveStrategy, diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestInstances.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestInstances.kt new file mode 100644 index 0000000..d994146 --- /dev/null +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestInstances.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.kswift.plugin.associatedenum + +object TestInstances { + val hasChar = HasChar('a') + val hasEnum = HasEnum(OwnEnum.A) + val hasFunction = HasFunction { i, l, s -> "$i $l $s" } + val hasNullableListNull = HasNullableOuterList(null) + val hasNullableList = HasNullableOuterList(emptyList()) + val hasInnerList = HasInnerList(listOf(listOf(true, false), listOf(true))) + val hasInnerNullable = HasInnerNullable(listOf(listOf(true, null), listOf(null))) +} diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt similarity index 87% rename from sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt rename to kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt index 4f27d65..c913f5a 100644 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestingSealed.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt @@ -1,4 +1,10 @@ -package com.icerockdev.library.associatedenum +/* + * Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.kswift.plugin.associatedenum + +import org.jetbrains.kotlin.kotlinx.collections.immutable.ImmutableList @Suppress("LongLine") sealed interface TestingSealed @@ -6,6 +12,7 @@ data class HasChar(val mychar: Char) : TestingSealed data class HasEnum(val myenum: OwnEnum) : TestingSealed data class HasFunction(val myfunc: (Int, List, String) -> String) : TestingSealed data class HasNullableOuterList(val innerList: List>?) : TestingSealed +data class HasImmutableList(val innerList: ImmutableList) : TestingSealed data class HasInnerList(val innerList: List>) : TestingSealed data class HasNullableInnerList(val innerList: List?>) : TestingSealed data class HasInnerNullable(val innerList: List>) : TestingSealed @@ -40,14 +47,19 @@ data class HasOwnClass(val ownClass: OwnClass) : TestingSealed data class HasOwnClassWithGeneric(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed data class HasOwnClassWithGenericAny(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed data class HasOwnClassWithGenericEnum(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed -data class HasOwnClassWithGenericInnerMap(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed +data class HasOwnClassWithGenericInnerMap(val ownClassWithGeneric: OwnHasGeneric>) : + TestingSealed data class HasOwnClassWithGenericInnerPair( val ownClassWithGeneric: OwnHasGeneric>, ) : TestingSealed -data class HasOwnClassWithGenericInnerSet(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed -data class HasOwnClassWithGenericNested(val ownClassWithGeneric: OwnHasGeneric>) : TestingSealed -data class HasOwnClassWithGenericNullable(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed -data class HasOwnClassWithGenericThrowable(val ownClassWithGeneric: OwnHasGeneric) : TestingSealed +data class HasOwnClassWithGenericInnerSet(val ownClassWithGeneric: OwnHasGeneric>) : + TestingSealed +data class HasOwnClassWithGenericNested(val ownClassWithGeneric: OwnHasGeneric>) : + TestingSealed +data class HasOwnClassWithGenericNullable(val ownClassWithGeneric: OwnHasGeneric) : + TestingSealed +data class HasOwnClassWithGenericThrowable(val ownClassWithGeneric: OwnHasGeneric) : + TestingSealed data class HasOwnClassWithGenericWildcard(val ownClassWithGeneric: OwnHasGeneric<*>) : TestingSealed data class HasPairGeneric(val pair: Pair) : TestingSealed data class HasPairBool(val pair: Pair) : TestingSealed diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestInstances.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestInstances.kt deleted file mode 100644 index 4b98724..0000000 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/associatedenum/TestInstances.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.icerockdev.library.associatedenum - -import com.icerockdev.library.associatedenum.HasChar -import com.icerockdev.library.associatedenum.HasEnum -import com.icerockdev.library.associatedenum.HasFunction -import com.icerockdev.library.associatedenum.HasInnerList -import com.icerockdev.library.associatedenum.HasInnerNullable -import com.icerockdev.library.associatedenum.HasNullableOuterList -import com.icerockdev.library.associatedenum.OwnEnum - -object TestInstances { - val hasChar = HasChar('a') - val hasEnum = HasEnum(OwnEnum.A) - val hasFunction = HasFunction { i, l, s -> "$i $l $s" } - val hasNullableListNull = HasNullableOuterList(null) - val hasNullableList = HasNullableOuterList(emptyList()) - val hasInnerList = HasInnerList(listOf(listOf(true, false), listOf(true))) - val hasInnerNullable = HasInnerNullable(listOf(listOf(true, null), listOf(null))) -} From da38c3ed1eaf1739c2b31ec0d3b2bc728c5341ca Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 5 Jun 2023 13:30:52 -0700 Subject: [PATCH 15/16] fixes for inner casting --- .../plugin/feature/associatedenum/OtherExt.kt | 1 - .../SealedToSwiftAssociatedEnumFeatureTest.kt | 40 ++++++++++++------ .../plugin/associatedenum/TestingSealed.kt | 2 +- .../src/test/resources/associated-enum.klib | Bin 36589 -> 43319 bytes 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt index c8c5ef9..2e16e5a 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/OtherExt.kt @@ -71,7 +71,6 @@ private fun String.kotlinPrimitiveTypeNameToSwift( typeParameters: List, ): TypeName { require(this.startsWith("kotlin/") || this.startsWith("kotlinx/collections/immutable/")) - println(this) return when (this) { "kotlin/Char" -> DeclaredTypeName.typeName("Swift.Character") "kotlin/Comparable" -> DeclaredTypeName.typeName("Swift.Comparable") diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index a46ec5f..8e85f2b 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -47,7 +47,7 @@ class SealedToSwiftAssociatedEnumFeatureTest { import shared /** - * selector: ClassContext/associated-enum/com/icerockdev/library/associatedenum/LoadingState */ + * selector: ClassContext/My_Application:shared/dev/icerock/moko/kswift/plugin/associatedenum/LoadingState */ public enum LoadingStateKs { case errorOnLoad(error: String) @@ -85,7 +85,7 @@ public enum LoadingStateKs { } /** - * selector: ClassContext/associated-enum/com/icerockdev/library/associatedenum/TestingSealed */ + * selector: ClassContext/My_Application:shared/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed */ public enum TestingSealedKs { case hasChar(mychar: Character) @@ -95,6 +95,9 @@ public enum TestingSealedKs { [KotlinBoolean], String ) -> String) + case hasImmutableList(innerImmutableList: [String]) + case hasImmutableListNullable(innerImmutableListNullable: [String]?) + case hasImmutableListNullableInner(innerImmutableListNullableInner: [String?]?) case hasInnerList(innerList: [[KotlinBoolean]]) case hasInnerNullable(innerList: [[KotlinBoolean?]]) case hasListInt(hasGeneric: [KotlinInt]) @@ -144,6 +147,12 @@ public enum TestingSealedKs { return shared.HasEnum(myenum: myenum) case .hasFunction(let myfunc): return shared.HasFunction(myfunc: myfunc) + case .hasImmutableList(let innerImmutableList): + return shared.HasImmutableList(innerImmutableList: innerImmutableList) + case .hasImmutableListNullable(let innerImmutableListNullable): + return shared.HasImmutableListNullable(innerImmutableListNullable: innerImmutableListNullable != nil ? innerImmutableListNullable : nil) + case .hasImmutableListNullableInner(let innerImmutableListNullableInner): + return shared.HasImmutableListNullableInner(innerImmutableListNullableInner: innerImmutableListNullableInner != nil ? innerImmutableListNullableInner : nil) case .hasInnerList(let innerList): return shared.HasInnerList(innerList: innerList) case .hasInnerNullable(let innerList): @@ -234,37 +243,43 @@ public enum TestingSealedKs { self = .hasEnum(myenum: obj.myenum) } else if let obj = obj as? shared.HasFunction { self = .hasFunction(myfunc: obj.myfunc) + } else if let obj = obj as? shared.HasImmutableList { + self = .hasImmutableList(innerImmutableList: obj.innerImmutableList as! [Swift.String]) + } else if let obj = obj as? shared.HasImmutableListNullable { + self = .hasImmutableListNullable(innerImmutableListNullable: obj.innerImmutableListNullable != nil ? obj.innerImmutableListNullable as! [Swift.String] : nil) + } else if let obj = obj as? shared.HasImmutableListNullableInner { + self = .hasImmutableListNullableInner(innerImmutableListNullableInner: obj.innerImmutableListNullableInner != nil ? obj.innerImmutableListNullableInner as! [Swift.String?] : nil) } else if let obj = obj as? shared.HasInnerList { self = .hasInnerList(innerList: obj.innerList as! [[shared.KotlinBoolean]]) } else if let obj = obj as? shared.HasInnerNullable { - self = .hasInnerNullable(innerList: obj.innerList as! [[shared.KotlinBoolean]]) + self = .hasInnerNullable(innerList: obj.innerList as! [[shared.KotlinBoolean?]]) } else if let obj = obj as? shared.HasListInt { self = .hasListInt(hasGeneric: obj.hasGeneric as! [shared.KotlinInt]) } else if let obj = obj as? shared.HasListIntNullable { - self = .hasListIntNullable(hasGeneric: obj.hasGeneric as! [shared.KotlinInt]) + self = .hasListIntNullable(hasGeneric: obj.hasGeneric as! [shared.KotlinInt?]) } else if let obj = obj as? shared.HasListOwn { self = .hasListOwn(hasGeneric: obj.hasGeneric as! [shared.OwnClass]) } else if let obj = obj as? shared.HasListString { self = .hasListString(hasGeneric: obj.hasGeneric as! [Swift.String]) } else if let obj = obj as? shared.HasListStringNullable { - self = .hasListStringNullable(hasGeneric: obj.hasGeneric as! [Swift.String]) + self = .hasListStringNullable(hasGeneric: obj.hasGeneric as! [Swift.String?]) } else if let obj = obj as? shared.HasListStringOuterNullable { - self = .hasListStringOuterNullable(hasGeneric: obj.hasGeneric != nil ? obj.hasGeneric as! [[Swift.String]] : nil) + self = .hasListStringOuterNullable(hasGeneric: obj.hasGeneric != nil ? obj.hasGeneric as! [Swift.String] : nil) } else if let obj = obj as? shared.HasMap { self = .hasMap(map: obj.map as! [Swift.String : shared.KotlinInt]) } else if let obj = obj as? shared.HasMapNullableOuter { self = .hasMapNullableOuter(map: obj.map != nil ? obj.map as! [Swift.String : shared.KotlinInt] : nil) } else if let obj = obj as? shared.HasMapNullableParams { - self = .hasMapNullableParams(map: obj.map as! [Swift.String : shared.KotlinInt]) + self = .hasMapNullableParams(map: obj.map as! [Swift.String : shared.KotlinInt?]) } else if let obj = obj as? shared.HasMultipleOwnParams { self = .hasMultipleOwnParams(p1: obj.p1, p2: obj.p2) } else if let obj = obj as? shared.HasNestedGeneric { self = .hasNestedGeneric(nested: obj.nested as! [shared.KotlinPair]) } else if let obj = obj as? shared.HasNullableInnerList { - self = .hasNullableInnerList(innerList: obj.innerList as! [[shared.KotlinBoolean]]) + self = .hasNullableInnerList(innerList: obj.innerList as! [[shared.KotlinBoolean]?]) } else if let obj = obj as? shared.HasNullableOuterList { - self = .hasNullableOuterList(innerList: obj.innerList != nil ? obj.innerList as! [[[shared.KotlinBoolean]]] : nil) + self = .hasNullableOuterList(innerList: obj.innerList != nil ? obj.innerList as! [[shared.KotlinBoolean]] : nil) } else if let obj = obj as? shared.HasOtherNullables { self = .hasOtherNullables(mystring: obj.mystring as String, optstring: obj.optstring != nil ? obj.optstring! as String : nil, @@ -305,13 +320,13 @@ public enum TestingSealedKs { } else if let obj = obj as? shared.HasSet { self = .hasSet(myset: obj.myset as! Set) } else if let obj = obj as? shared.HasSetNullableInt { - self = .hasSetNullableInt(myset: obj.myset as! Set) + self = .hasSetNullableInt(myset: obj.myset as! Set) } else if let obj = obj as? shared.HasSetNullableOuter { - self = .hasSetNullableOuter(myset: obj.myset != nil ? obj.myset as! Set> : nil) + self = .hasSetNullableOuter(myset: obj.myset != nil ? obj.myset as! Set : nil) } else if let obj = obj as? shared.HasSetString { self = .hasSetString(myset: obj.myset as! Set) } else if let obj = obj as? shared.HasSetStringNullable { - self = .hasSetStringNullable(myset: obj.myset as! Set) + self = .hasSetStringNullable(myset: obj.myset as! Set) } else if let obj = obj as? shared.HasSomeNullables { self = .hasSomeNullables(myint: obj.myint, myintopt: obj.myintopt?.int32Value, @@ -332,6 +347,7 @@ public enum TestingSealedKs { } """ + println(appendable.toString().split("\n").filter { it.contains("mmutable") }.joinToString(separator = "\n")) assertEquals(expected, appendable.toString()) } } diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt index c913f5a..c07028d 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/associatedenum/TestingSealed.kt @@ -4,7 +4,7 @@ package dev.icerock.moko.kswift.plugin.associatedenum -import org.jetbrains.kotlin.kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableList @Suppress("LongLine") sealed interface TestingSealed diff --git a/kswift-gradle-plugin/src/test/resources/associated-enum.klib b/kswift-gradle-plugin/src/test/resources/associated-enum.klib index 48b521d44de2c66f858da2c14673ba83ef6a2cd4..18ed99393cf0f7af6b8b6be638c8c1e69fdf4158 100644 GIT binary patch literal 43319 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 literal 36589 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 Ie)H=80a+|Y+5i9m From 5bdd94747a794723798249b5069c425bed743922 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Mon, 5 Jun 2023 14:08:04 -0700 Subject: [PATCH 16/16] fixes for optional casting --- .../plugin/feature/associatedenum/AssociatedEnumCase.kt | 2 ++ .../feature/associatedenum/ParameterizedTypeNameExt.kt | 9 +++++---- .../kswift/plugin/feature/associatedenum/TypeNameExt.kt | 1 - .../plugin/SealedToSwiftAssociatedEnumFeatureTest.kt | 5 ++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt index 87852bf..ab26c3e 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/AssociatedEnumCase.kt @@ -169,6 +169,8 @@ data class AssociatedEnumCase( private fun String.toNSString(): String = if (this == "String" || this == "Swift.String") { "NSString" + } else if (this == "String?" || this == "Swift.String?") { + "NSString?" } else { this } diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt index 3a571ba..1b6dc67 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/ParameterizedTypeNameExt.kt @@ -10,9 +10,8 @@ internal fun ParameterizedTypeName.toArrayCaster( .plus( if (optional) "obj.$paramName != nil ? " else "", ) - .plus("obj.$paramName as! [") - .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) - .plus("]") + .plus("obj.$paramName as! ") + .plus(this.unwrapOptional().kotlinInteropTypeWithFallback) .plus(if (optional) " : nil" else "") internal fun ParameterizedTypeName.toSetCaster( @@ -23,7 +22,9 @@ internal fun ParameterizedTypeName.toSetCaster( if (optional) "obj.$paramName != nil ? " else "", ) .plus("obj.$paramName as! Set<") - .plus(this.typeArguments[0].kotlinInteropTypeWithFallback) + .plus( + (this.unwrapOptional() as ParameterizedTypeName).typeArguments[0].kotlinInteropTypeWithFallback + ) .plus(">") .plus(if (optional) " : nil" else "") diff --git a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt index 7f73ffb..fc3909f 100644 --- a/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt +++ b/kswift-gradle-plugin/src/main/kotlin/dev/icerock/moko/kswift/plugin/feature/associatedenum/TypeNameExt.kt @@ -22,7 +22,6 @@ internal val TypeName.kotlinInteropTypeWithFallback: String ?: this.kotlinInteropFromSwiftType ?: this.name ) - .replace("?", "") internal fun TypeName.generateKotlinConstructorIfNecessaryForParameter(paramName: String): String { return when { diff --git a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt index 8e85f2b..7b90e4b 100644 --- a/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt +++ b/kswift-gradle-plugin/src/test/kotlin/dev/icerock/moko/kswift/plugin/SealedToSwiftAssociatedEnumFeatureTest.kt @@ -212,9 +212,9 @@ public enum TestingSealedKs { case .hasPairFloat(let pair): return shared.HasPairFloat(pair: KotlinPair(first: KotlinFloat(value: pair.0), second: pair.1 != nil ? KotlinFloat(value: pair.1!) : nil)) case .hasPairGeneric(let pair): - return shared.HasPairGeneric(pair: KotlinPair(first: KotlinUByte(value: pair.0), second: pair.1 != nil ? pair.1 : nil)) + return shared.HasPairGeneric(pair: KotlinPair(first: KotlinUByte(value: pair.0), second: pair.1 != nil ? pair.1 : nil)) case .hasPairString(let pair): - return shared.HasPairString(pair: KotlinPair(first: pair.0 as NSString, second: pair.1 != nil ? pair.1! as NSString : nil)) + return shared.HasPairString(pair: KotlinPair(first: pair.0 as NSString, second: pair.1 != nil ? pair.1! as NSString : nil)) case .hasSet(let myset): return shared.HasSet(myset: myset) case .hasSetNullableInt(let myset): @@ -347,7 +347,6 @@ public enum TestingSealedKs { } """ - println(appendable.toString().split("\n").filter { it.contains("mmutable") }.joinToString(separator = "\n")) assertEquals(expected, appendable.toString()) } }