Skip to content

Commit ef57c81

Browse files
committed
feat: Traits!!!
1 parent 34101df commit ef57c81

18 files changed

Lines changed: 345 additions & 14 deletions

mmrpc-definition/src/commonMain/kotlin/Definition.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,25 @@ data class ScalarDefinition(
160160
val type: ScalarDefinition? = null,
161161
) : TypeDefinition()
162162

163+
@Serializable
164+
@SerialName("trait")
165+
data class TraitDefinition(
166+
override val canonicalName: CanonicalName,
167+
override val description: String = "",
168+
override val metadata: List<MetadataUsage> = emptyList(),
169+
170+
val discriminator: String = "type",
171+
val fields: List<FieldDefinition> = emptyList(),
172+
) : TypeDefinition()
173+
163174
@Serializable
164175
@SerialName("struct")
165176
data class StructDefinition(
166177
override val canonicalName: CanonicalName,
167178
override val description: String = "",
168179
override val metadata: List<MetadataUsage> = emptyList(),
169180

181+
val traits: List<TraitDefinition> = emptyList(),
170182
val fields: List<FieldDefinition> = emptyList(),
171183
) : TypeDefinition()
172184

mmrpc-definition/src/commonMain/kotlin/builder/StructDefinitionBuilder.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package org.cufy.mmrpc.builder
22

3-
import org.cufy.mmrpc.FieldDefinition
4-
import org.cufy.mmrpc.Marker2
5-
import org.cufy.mmrpc.StructDefinition
6-
import org.cufy.mmrpc.Unnamed
3+
import org.cufy.mmrpc.*
74
import org.cufy.mmrpc.internal.VARARG_VARIANTS_DEPRECATED_MSG
85

96
////////////////////////////////////////
@@ -13,9 +10,15 @@ typealias StructDefinitionBlock = context(StructDefinitionBuilder) () -> Unit
1310
@Marker2
1411
class StructDefinitionBuilder :
1512
FieldDefinitionContainerBuilder,
13+
TraitDefinitionContainerBuilder,
1614
ElementDefinitionBuilder() {
15+
val traits = mutableListOf<Unnamed<TraitDefinition>>()
1716
val fields = mutableListOf<Unnamed<FieldDefinition>>()
1817

18+
override fun addTraitDefinition(value: Unnamed<TraitDefinition>) {
19+
traits += value
20+
}
21+
1922
override fun addFieldDefinition(value: Unnamed<FieldDefinition>) {
2023
fields += value
2124
}
@@ -26,6 +29,9 @@ class StructDefinitionBuilder :
2629
canonicalName = cn,
2730
description = this.description,
2831
metadata = this.metadata.toList(),
32+
traits = this.traits.mapIndexed { i, it ->
33+
it.get(cn, name = "trait$i")
34+
},
2935
fields = this.fields.mapIndexed { i, it ->
3036
it.get(cn, name = "field$i")
3137
},
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.cufy.mmrpc.builder
2+
3+
import org.cufy.mmrpc.FieldDefinition
4+
import org.cufy.mmrpc.Marker2
5+
import org.cufy.mmrpc.TraitDefinition
6+
import org.cufy.mmrpc.Unnamed
7+
8+
////////////////////////////////////////
9+
10+
typealias TraitDefinitionBlock = context(TraitDefinitionBuilder) () -> Unit
11+
12+
@Marker2
13+
class TraitDefinitionBuilder :
14+
FieldDefinitionContainerBuilder,
15+
ElementDefinitionBuilder() {
16+
var discriminator: String = "type"
17+
val fields = mutableListOf<Unnamed<FieldDefinition>>()
18+
19+
override fun addFieldDefinition(value: Unnamed<FieldDefinition>) {
20+
fields += value
21+
}
22+
23+
fun build(): TraitDefinition {
24+
val cn = buildCanonicalName()
25+
return TraitDefinition(
26+
canonicalName = cn,
27+
description = this.description,
28+
metadata = this.metadata.toList(),
29+
discriminator = this.discriminator,
30+
fields = this.fields.mapIndexed { i, it ->
31+
it.get(cn, name = "field$i")
32+
},
33+
)
34+
}
35+
}
36+
37+
////////////////////////////////////////
38+
39+
@Marker2
40+
val trait = trait()
41+
42+
@Marker2
43+
fun trait(
44+
block: TraitDefinitionBlock = {},
45+
) = Unnamed { ns, name ->
46+
TraitDefinitionBuilder()
47+
.also { it.name = name ?: return@also }
48+
.also { it.namespace = ns }
49+
.apply(block)
50+
.build()
51+
}
52+
53+
////////////////////////////////////////
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.cufy.mmrpc.builder
2+
3+
import org.cufy.mmrpc.Marker2
4+
import org.cufy.mmrpc.TraitDefinition
5+
import org.cufy.mmrpc.Unnamed
6+
import kotlin.jvm.JvmName
7+
8+
////////////////////////////////////////
9+
10+
@Marker2
11+
interface TraitDefinitionContainerBuilder {
12+
fun addTraitDefinition(value: Unnamed<TraitDefinition>)
13+
}
14+
15+
////////////////////////////////////////
16+
17+
context(ctx: TraitDefinitionContainerBuilder)
18+
operator fun Unnamed<TraitDefinition>.unaryPlus() {
19+
ctx.addTraitDefinition(this)
20+
}
21+
22+
@JvmName("Iterable_Unnamed_TraitDefinition_unaryPlus")
23+
context(ctx: TraitDefinitionContainerBuilder)
24+
operator fun Iterable<Unnamed<TraitDefinition>>.unaryPlus() {
25+
for (it in this) +it
26+
}
27+
28+
context(ctx: TraitDefinitionContainerBuilder)
29+
operator fun TraitDefinition.unaryPlus() {
30+
+Unnamed(this)
31+
}
32+
33+
context(ctx: TraitDefinitionContainerBuilder)
34+
operator fun Iterable<TraitDefinition>.unaryPlus() {
35+
for (it in this) +Unnamed(it)
36+
}
37+
38+
////////////////////////////////////////

mmrpc-definition/src/commonMain/kotlin/collect.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,14 @@ private fun ElementDefinition.collectChildren(): Sequence<ElementDefinition> {
8181
type?.let { yieldAll(it.collect()) }
8282
}
8383

84+
is TraitDefinition -> sequence {
85+
yieldAll(metadata.asSequence().flatMap { it.collect() })
86+
yieldAll(fields.asSequence().flatMap { it.collect() })
87+
}
88+
8489
is StructDefinition -> sequence {
8590
yieldAll(metadata.asSequence().flatMap { it.collect() })
91+
yieldAll(traits.asSequence().flatMap { it.collect() })
8692
yieldAll(fields.asSequence().flatMap { it.collect() })
8793
}
8894

mmrpc-definition/src/commonMain/kotlin/compact/CompactElementDefinition.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fun ElementDefinition.toCompact(): CompactElementDefinition {
3030
is ProtocolDefinition -> toCompact()
3131
is RoutineDefinition -> toCompact()
3232
is ScalarDefinition -> toCompact()
33+
is TraitDefinition -> toCompact()
3334
is StructDefinition -> toCompact()
3435
is TupleDefinition -> toCompact()
3536
is UnionDefinition -> toCompact()
@@ -50,6 +51,7 @@ fun CompactElementDefinition.strip(): CompactElementDefinition {
5051
is CompactInterDefinition -> copy(description = "")
5152
is CompactOptionalDefinition -> copy(description = "")
5253
is CompactScalarDefinition -> copy(description = "")
54+
is CompactTraitDefinition -> copy(description = "")
5355
is CompactStructDefinition -> copy(description = "")
5456
is CompactTupleDefinition -> copy(description = "")
5557
is CompactUnionDefinition -> copy(description = "")
@@ -72,6 +74,7 @@ fun CompactElementDefinition.inflateOrNull(
7274
is CompactProtocolDefinition -> inflateOrNull(onLookup)
7375
is CompactRoutineDefinition -> inflateOrNull(onLookup)
7476
is CompactScalarDefinition -> inflateOrNull(onLookup)
77+
is CompactTraitDefinition -> inflateOrNull(onLookup)
7578
is CompactStructDefinition -> inflateOrNull(onLookup)
7679
is CompactTupleDefinition -> inflateOrNull(onLookup)
7780
is CompactUnionDefinition -> inflateOrNull(onLookup)

mmrpc-definition/src/commonMain/kotlin/compact/CompactStructDefinition.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ package org.cufy.mmrpc.compact
22

33
import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable
5-
import org.cufy.mmrpc.CanonicalName
6-
import org.cufy.mmrpc.ElementDefinition
7-
import org.cufy.mmrpc.FieldDefinition
8-
import org.cufy.mmrpc.StructDefinition
5+
import org.cufy.mmrpc.*
96

107
@Suppress("PropertyName")
118
@Serializable
@@ -15,6 +12,7 @@ data class CompactStructDefinition(
1512
override val description: String = "",
1613
override val metadata: List<CompactMetadataUsage> = emptyList(),
1714

15+
val traits_ref: List<CanonicalName> = emptyList(),
1816
val fields_ref: List<CanonicalName> = emptyList(),
1917
) : CompactElementDefinition
2018

@@ -23,6 +21,7 @@ fun StructDefinition.toCompact(): CompactStructDefinition {
2321
canonical_name = this.canonicalName,
2422
description = this.description,
2523
metadata = this.metadata.map { it.toCompact() },
24+
traits_ref = this.traits.map { it.canonicalName },
2625
fields_ref = this.fields.map { it.canonicalName },
2726
)
2827
}
@@ -36,6 +35,13 @@ fun CompactStructDefinition.inflateOrNull(
3635
metadata = this.metadata.map {
3736
it.inflateOrNull(onLookup) ?: return null
3837
},
38+
traits = this.traits_ref.map {
39+
val item = onLookup(it) ?: return null
40+
require(item is TraitDefinition) {
41+
"<struct>.traits_ref must point to a TraitDefinition"
42+
}
43+
item
44+
},
3945
fields = this.fields_ref.map {
4046
val item = onLookup(it) ?: return null
4147
require(item is FieldDefinition) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.cufy.mmrpc.compact
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
import org.cufy.mmrpc.CanonicalName
6+
import org.cufy.mmrpc.ElementDefinition
7+
import org.cufy.mmrpc.FieldDefinition
8+
import org.cufy.mmrpc.TraitDefinition
9+
10+
@Suppress("PropertyName")
11+
@Serializable
12+
@SerialName("trait")
13+
data class CompactTraitDefinition(
14+
override val canonical_name: CanonicalName,
15+
override val description: String = "",
16+
override val metadata: List<CompactMetadataUsage> = emptyList(),
17+
18+
val discriminator: String,
19+
val fields_ref: List<CanonicalName> = emptyList(),
20+
) : CompactElementDefinition
21+
22+
fun TraitDefinition.toCompact(): CompactTraitDefinition {
23+
return CompactTraitDefinition(
24+
canonical_name = this.canonicalName,
25+
description = this.description,
26+
metadata = this.metadata.map { it.toCompact() },
27+
discriminator = this.discriminator,
28+
fields_ref = this.fields.map { it.canonicalName },
29+
)
30+
}
31+
32+
fun CompactTraitDefinition.inflateOrNull(
33+
onLookup: (CanonicalName) -> ElementDefinition?,
34+
): TraitDefinition? {
35+
return TraitDefinition(
36+
canonicalName = this.canonical_name,
37+
description = this.description,
38+
metadata = this.metadata.map {
39+
it.inflateOrNull(onLookup) ?: return null
40+
},
41+
discriminator = this.discriminator,
42+
fields = this.fields_ref.map {
43+
val item = onLookup(it) ?: return null
44+
require(item is FieldDefinition) {
45+
"<trait>.fields_ref must point to a FieldDefinition"
46+
}
47+
item
48+
},
49+
)
50+
}

mmrpc-gen-kotlin/src/commonMain/kotlin/Declarations.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,8 @@ enum class UnionStrategy {
109109
SEALED_INTERFACE,
110110
WRAPPER_SEALED_INTERFACE,
111111
}
112+
113+
enum class TraitStrategy {
114+
INTERFACE,
115+
SEALED_INTERFACE,
116+
}

mmrpc-gen-kotlin/src/commonMain/kotlin/common/createLiteralCode.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ fun createLiteralCode(element: TypeDefinition, literal: Literal): CodeBlock {
4949
is StructLiteral -> createLiteralCodeOfUnion(element, literal)
5050
else -> fail(TAG, element) { "illegal value: $literal" }
5151
}
52+
53+
is TraitDefinition -> when (literal) {
54+
is StructLiteral -> createLiteralCodeOfTrait(element, literal)
55+
else -> fail(TAG, element) { "illegal value: $literal" }
56+
}
5257
}
5358
}
5459

@@ -232,3 +237,27 @@ private fun createLiteralCodeOfUnion(element: UnionDefinition, literal: StructLi
232237
}
233238
}
234239
}
240+
241+
context(ctx: GenContext)
242+
private fun createLiteralCodeOfTrait(element: TraitDefinition, literal: StructLiteral): CodeBlock {
243+
val structs = element.collectStructs()
244+
245+
if (structs.size == 1)
246+
return createLiteralCodeOfStruct(structs.single(), literal)
247+
248+
// extracting the discriminator from `literal`
249+
val canonicalNameLiteral = literal.value[element.discriminator]
250+
canonicalNameLiteral ?: fail(TAG, element) { "illegal value: $literal (no discriminator)" }
251+
252+
if (canonicalNameLiteral !is StringLiteral)
253+
fail(TAG, element) { "illegal value: $literal (non-string discriminator)" }
254+
255+
val canonicalNameValue = canonicalNameLiteral.value
256+
257+
// finding the suitable struct from the types of the union
258+
val winner = structs.find { it.canonicalName.value == canonicalNameValue }
259+
winner ?: fail(TAG, element) { "illegal value: $literal (unknown discriminator value)" }
260+
261+
// delegating literal creation to the found struct
262+
return createLiteralCodeOfStruct(winner, literal)
263+
}

0 commit comments

Comments
 (0)