Skip to content

Continuous improvements: Tuples, TypeUnions, descriptor extraction #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Versions {
const val kotlinxSerialization = "1.3.2"
const val ksp = "1.6.10-1.0.4"

const val kotest = "5.1.0"
const val kotest = "5.2.3"
}


Expand Down
12 changes: 6 additions & 6 deletions docs/code/example/example-tuple-01.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ data class SimpleTypes(
element(SimpleTypes::privateMember)
}
) {
override fun tupleConstructor(elements: List<*>): SimpleTypes {
override fun tupleConstructor(elements: Iterator<*>): SimpleTypes {
// When deserializing, the elements will be available as a list, in the order defined
return SimpleTypes(
elements[0] as String,
elements[1] as Int,
elements[2] as Double,
elements[3] as Boolean,
elements[4] as String,
elements.next() as String,
elements.next() as Int,
elements.next() as Double,
elements.next() as Boolean,
elements.next() as String,
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/code/example/example-tuple-02.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ data class OptionalFields(
element(OptionalFields::nullableOptionalString)
}
) {
override fun tupleConstructor(elements: List<*>): OptionalFields {
override fun tupleConstructor(elements: Iterator<*>): OptionalFields {
val iter = elements.iterator()
return OptionalFields(
iter.next() as String,
Expand Down
8 changes: 4 additions & 4 deletions docs/code/example/example-tuple-03.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ data class Coordinates(
element(Coordinates::z)
}
) {
override fun tupleConstructor(elements: List<*>): Coordinates {
override fun tupleConstructor(elements: Iterator<*>): Coordinates {
return Coordinates(
elements[0] as Int,
elements[1] as Int,
elements[2] as Int,
elements.next() as Int,
elements.next() as Int,
elements.next() as Int,
)
}
}
Expand Down
13 changes: 10 additions & 3 deletions docs/code/test/PolymorphismTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ class PolymorphismTest {
.shouldBe(
// language=TypeScript
"""
|export type Project = Project.DeprecatedProject | Project.OProj;
|export type Project =
| | Project.DeprecatedProject
| | Project.OProj;
|
|export namespace Project {
| export enum Type {
Expand Down Expand Up @@ -108,7 +110,10 @@ class PolymorphismTest {
.shouldBe(
// language=TypeScript
"""
|export type Dog = Dog.Golden | Dog.Mutt | Dog.NovaScotia;
|export type Dog =
| | Dog.Golden
| | Dog.Mutt
| | Dog.NovaScotia;
|
|export namespace Dog {
| export enum Type {
Expand Down Expand Up @@ -185,7 +190,9 @@ class PolymorphismTest {
.shouldBe(
// language=TypeScript
"""
|export type Response = Response.EmptyResponse | Response.TextResponse;
|export type Response =
| | Response.EmptyResponse
| | Response.TextResponse;
|
|export namespace Response {
| export enum Type {
Expand Down
13 changes: 10 additions & 3 deletions docs/polymorphism.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ fun main() {
> You can get the full code [here](./code/example/example-polymorphic-sealed-class-01.kt).

```typescript
export type Project = Project.DeprecatedProject | Project.OProj;
export type Project =
| Project.DeprecatedProject
| Project.OProj;

export namespace Project {
export enum Type {
Expand Down Expand Up @@ -231,7 +233,10 @@ fun main() {
> You can get the full code [here](./code/example/example-polymorphic-sealed-class-02.kt).

```typescript
export type Dog = Dog.Golden | Dog.Mutt | Dog.NovaScotia;
export type Dog =
| Dog.Golden
| Dog.Mutt
| Dog.NovaScotia;

export namespace Dog {
export enum Type {
Expand Down Expand Up @@ -322,7 +327,9 @@ fun main() {
> You can get the full code [here](./code/example/example-polymorphic-objects-01.kt).

```typescript
export type Response = Response.EmptyResponse | Response.TextResponse;
export type Response =
| Response.EmptyResponse
| Response.TextResponse;

export namespace Response {
export enum Type {
Expand Down
22 changes: 11 additions & 11 deletions docs/tuples.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ data class SimpleTypes(
element(SimpleTypes::privateMember)
}
) {
override fun tupleConstructor(elements: List<*>): SimpleTypes {
override fun tupleConstructor(elements: Iterator<*>): SimpleTypes {
// When deserializing, the elements will be available as a list, in the order defined
return SimpleTypes(
elements[0] as String,
elements[1] as Int,
elements[2] as Double,
elements[3] as Boolean,
elements[4] as String,
elements.next() as String,
elements.next() as Int,
elements.next() as Double,
elements.next() as Boolean,
elements.next() as String,
)
}
}
Expand Down Expand Up @@ -111,7 +111,7 @@ data class OptionalFields(
element(OptionalFields::nullableOptionalString)
}
) {
override fun tupleConstructor(elements: List<*>): OptionalFields {
override fun tupleConstructor(elements: Iterator<*>): OptionalFields {
val iter = elements.iterator()
return OptionalFields(
iter.next() as String,
Expand Down Expand Up @@ -154,11 +154,11 @@ data class Coordinates(
element(Coordinates::z)
}
) {
override fun tupleConstructor(elements: List<*>): Coordinates {
override fun tupleConstructor(elements: Iterator<*>): Coordinates {
return Coordinates(
elements[0] as Int,
elements[1] as Int,
elements[2] as Int,
elements.next() as Int,
elements.next() as Int,
elements.next() as Int,
)
}
}
Expand Down
15 changes: 14 additions & 1 deletion modules/kxs-ts-gen-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ plugins {
buildsrc.convention.`maven-publish`
kotlin("plugin.serialization")
// id("org.jetbrains.reflekt")
id("io.kotest.multiplatform")
}

val kotlinxSerializationVersion = "1.3.2"
val kotestVersion = "5.2.2"

kotlin {

Expand Down Expand Up @@ -70,6 +72,12 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(kotlin("test"))

implementation("io.kotest:kotest-assertions-core:$kotestVersion")
implementation("io.kotest:kotest-assertions-json:$kotestVersion")
implementation("io.kotest:kotest-property:$kotestVersion")
implementation("io.kotest:kotest-framework-engine:$kotestVersion")
implementation("io.kotest:kotest-framework-datatest:$kotestVersion")
}
}
// val nativeMain by getting
Expand All @@ -81,6 +89,11 @@ kotlin {
implementation(kotlin("reflect"))
}
}
// val jvmTest by getting

val jvmTest by getting {
dependencies {
implementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ open class KxsTsGenerator(

open fun generate(vararg serializers: KSerializer<*>): String {
return serializers
.toSet()

// 1. get all SerialDescriptors from a KSerializer
.flatMap { serializer ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ abstract class KxsTsSourceCodeGenerator(
is TsDeclaration.TsEnum -> generateEnum(element)
is TsDeclaration.TsInterface -> generateInterface(element)
is TsDeclaration.TsNamespace -> generateNamespace(element)
is TsDeclaration.TsTypeAlias -> generateType(element)
is TsDeclaration.TsTypeUnion -> generateTypeUnion(element)
is TsDeclaration.TsTypeAlias -> generateTypeAlias(element)
is TsDeclaration.TsTuple -> generateTuple(element)
}
}

abstract fun generateEnum(enum: TsDeclaration.TsEnum): String
abstract fun generateInterface(element: TsDeclaration.TsInterface): String
abstract fun generateNamespace(namespace: TsDeclaration.TsNamespace): String
abstract fun generateType(element: TsDeclaration.TsTypeAlias): String
abstract fun generateTypeAlias(element: TsDeclaration.TsTypeAlias): String
abstract fun generateTypeUnion(element: TsDeclaration.TsTypeUnion): String
abstract fun generateTuple(tuple: TsDeclaration.TsTuple): String

abstract fun generateMapTypeReference(tsMap: TsLiteral.TsMap): String
Expand Down Expand Up @@ -117,17 +119,13 @@ abstract class KxsTsSourceCodeGenerator(
}


override fun generateType(element: TsDeclaration.TsTypeAlias): String {
val aliases =
element.typeRefs
.map { generateTypeReference(it) }
.sorted()
.joinToString(" | ")
override fun generateTypeAlias(element: TsDeclaration.TsTypeAlias): String {
val aliasedRef = generateTypeReference(element.typeRef)

return when (config.typeAliasTyping) {
KxsTsConfig.TypeAliasTypingConfig.None ->
"""
|export type ${element.id.name} = ${aliases};
|export type ${element.id.name} = ${aliasedRef};
""".trimMargin()
KxsTsConfig.TypeAliasTypingConfig.BrandTyping -> {

Expand All @@ -141,12 +139,31 @@ abstract class KxsTsSourceCodeGenerator(
}.joinToString("")

"""
|export type ${element.id.name} = $aliases & { __${brandType}__: void };
|export type ${element.id.name} = $aliasedRef & { __${brandType}__: void };
""".trimMargin()
}
}
}

override fun generateTypeUnion(element: TsDeclaration.TsTypeUnion): String {
return if (element.typeRefs.isEmpty()) {
"""
|export type ${element.id.name} = ${generatePrimitive(TsLiteral.Primitive.TsUnknown)};
""".trimMargin()
} else {
val aliases = element.typeRefs
.map { "${config.indent}| ${generateTypeReference(it)}" }
.sorted()
.joinToString("\n")

"""
¦export type ${element.id.name} =
¦$aliases;
""".trimMargin("¦")
}
}


override fun generateTuple(tuple: TsDeclaration.TsTuple): String {

val types = tuple.elements.joinToString(separator = ", ") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,25 @@ fun interface SerializerDescriptorsExtractor {
StructureKind.OBJECT -> descriptor.elementDescriptors

PolymorphicKind.SEALED,
PolymorphicKind.OPEN -> descriptor
.elementDescriptors
.filter { it.kind is PolymorphicKind }
.flatMap { it.elementDescriptors }
PolymorphicKind.OPEN ->
// Polymorphic descriptors have 2 elements, the 'type' and 'value' - we don't need either
// for generation, they're metadata that will be used later.
// The elements of 'value' are similarly unneeded, but their elements might contain new
// descriptors - so extract them
descriptor.elementDescriptors
.flatMap { it.elementDescriptors }
.flatMap { it.elementDescriptors }

// Example:
// com.application.Polymorphic<MySealedClass>
// ├── 'type' descriptor (ignore / it's a String, so check its elements, it doesn't hurt)
// └── 'value' descriptor (check elements...)
// ├── com.application.Polymorphic<Subclass1> (ignore)
// │ ├── Double (extract!)
// │ └── com.application.SomeOtherClass (extract!)
// └── com.application.Polymorphic<Subclass2> (ignore)
// ├── UInt (extract!)
// └── List<com.application.AnotherClass (extract!
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fun interface TsElementConverter {
}

// create type union and namespace
val subInterfaceTypeUnion = TsDeclaration.TsTypeAlias(
val subInterfaceTypeUnion = TsDeclaration.TsTypeUnion(
namespaceId,
subInterfaceRefs.keys
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ abstract class TupleSerializer<T>(
}
private val indexedTupleElements = tupleElements.associateBy { it.index }

abstract fun tupleConstructor(elements: List<*>): T
abstract fun tupleConstructor(elements: Iterator<*>): T

override val descriptor: SerialDescriptor = buildSerialDescriptor(
serialName = serialName,
Expand All @@ -123,7 +123,7 @@ abstract class TupleSerializer<T>(
generateSequence {
val index = decodeElementIndex(descriptor)
indexedTupleElements[index]?.decodeElement(this)
}.toList()
}.iterator()
}
return tupleConstructor(elements)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ sealed interface TsDeclaration : TsElement {
val id: TsElementId


/** A named reference to one or more other types. */
/** A named reference to another type. */
data class TsTypeAlias(
override val id: TsElementId,
val typeRef: TsTypeRef,
) : TsDeclaration


/** A named reference to one or more other types. */
data class TsTypeUnion(
override val id: TsElementId,
val typeRefs: Set<TsTypeRef>,
) : TsDeclaration {
constructor(id: TsElementId, typeRef: TsTypeRef, vararg typeRefs: TsTypeRef) :
this(id, typeRefs.toSet() + typeRef)
}
) : TsDeclaration


/** A [tuple type](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types). */
Expand Down
Loading