Skip to content

Commit

Permalink
Fix avro emitter + improve maven plugin (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
wilmveel authored Feb 21, 2025
1 parent 2979a8e commit d63e261
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 19 deletions.
15 changes: 14 additions & 1 deletion examples/maven-spring-custom/app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</dependencies>
<executions>
<execution>
<id>custom</id>
<id>todo</id>
<goals>
<goal>custom</goal>
</goals>
Expand All @@ -54,6 +54,19 @@
<split>true</split>
</configuration>
</execution>
<execution>
<id>spec</id>
<goals>
<goal>custom</goal>
</goals>
<configuration>
<input>classpath:test/spec.ws</input>
<output>${project.build.directory}/generated-sources/java/hello</output>
<emitterClass>community.flock.wirespec.example.maven.custom.emit.CustomEmitter</emitterClass>
<extension>java</extension>
<split>true</split>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package community.flock.wirespec.example.maven.custom.app;

import hello.CustomCustom;
import hello.TodoCustom;
import hello.TodoIdCustom;
import hello.TodoInputCustom;
Expand All @@ -15,7 +16,9 @@ class TodoController {

@GetMapping("/")
public List<String> list() {

return List.of(
CustomCustom.class.getName(),
TodoCustom.class.getName(),
TodoIdCustom.class.getName(),
TodoInputCustom.class.getName()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Custom {
custom: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import community.flock.wirespec.compiler.core.parse.Field
import community.flock.wirespec.compiler.core.parse.FieldIdentifier
import community.flock.wirespec.compiler.core.parse.Reference
import community.flock.wirespec.compiler.core.parse.Type
import community.flock.wirespec.compiler.core.parse.Union

object AvroConverter {

Expand Down Expand Up @@ -36,6 +37,8 @@ object AvroConverter {
is AvroModel.RecordType -> Reference.Custom(value = name, isNullable = isNullable)
is AvroModel.EnumType -> Reference.Custom(value = name, isNullable = isNullable)
is AvroModel.LogicalType -> AvroModel.SimpleType(value = type).toReference(isNullable)
is AvroModel.MapType -> Reference.Dict(reference = values.toReference(false), isNullable = isNullable)
is AvroModel.UnionType -> Reference.Custom(value = name, isNullable = isNullable)
}

private fun AvroModel.TypeList.toReference(): Reference {
Expand Down Expand Up @@ -67,11 +70,19 @@ object AvroConverter {
entries = symbols.toSet(),
)

fun AvroModel.Type.flatten(): AST = when (this) {
private fun AvroModel.UnionType.toUnion(name: String) = Union(
comment = null,
identifier = DefinitionIdentifier(name),
entries = this.type.map { it.toReference(false) }.toSet(),
)

fun AvroModel.Type.flatten(name: String = ""): AST = when (this) {
is AvroModel.SimpleType -> emptyList()
is AvroModel.RecordType -> listOf(toType()) + fields.flatMap { field -> field.type.flatMap { it.flatten() } }
is AvroModel.ArrayType -> items.flatten()
is AvroModel.RecordType -> listOf(toType()) + fields.flatMap { field -> field.type.flatMap { it.flatten(name) } }
is AvroModel.ArrayType -> items.flatten(name)
is AvroModel.EnumType -> listOf(toEnum())
is AvroModel.LogicalType -> emptyList()
is AvroModel.MapType -> values.flatten(name)
is AvroModel.UnionType -> listOf(toUnion(name))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ object AvroEmitter {
symbols = entries.toList()
)

fun Union.emit(): AvroModel.UnionType = AvroModel.UnionType(
name = identifier.value,
type = AvroModel.TypeList(entries.map { AvroModel.SimpleType(it.value) })
)

fun Reference.emit(ast: AST, hasEmitted: MutableList<String>): AvroModel.Type = when (this) {
is Reference.Dict -> TODO()
is Reference.Iterable -> TODO()
is Reference.Dict -> AvroModel.MapType(type = "map", values = reference.emit(ast, hasEmitted))
is Reference.Iterable -> AvroModel.ArrayType(type = "array", items = reference.emit(ast, hasEmitted))
is Reference.Primitive -> {
when (val type = type) {
is Reference.Primitive.Type.String -> AvroModel.SimpleType("string")
Expand All @@ -47,13 +52,9 @@ object AvroEmitter {
} else {
def.also { hasEmitted.add(def.identifier.value) }.emit(ast, hasEmitted)
}

is Enum -> def.emit()
is Channel -> TODO()
is Endpoint -> TODO()
is Refined -> TODO()
is Union -> TODO()
null -> AvroModel.SimpleType(value)
is Enum -> AvroModel.SimpleType(def.identifier.value)
is Refined -> AvroModel.SimpleType("string")
else -> AvroModel.SimpleType(value)
}
}

Expand All @@ -67,7 +68,6 @@ object AvroEmitter {
type = "array",
items = ref.reference.emit(ast, hasEmitted)
)

else -> ref.emit(ast, hasEmitted)
}

Expand Down Expand Up @@ -97,6 +97,7 @@ object AvroEmitter {
when (it) {
is Type -> it.emit(ast, hasEmitted)
is Enum -> it.emit()
is Union -> it.emit()
else -> null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ object AvroModel {
val items: Type,
) : Type

@Serializable
data class MapType(
val type: String,
val values: Type,
) : Type

@Serializable
data class EnumType(
val type: String,
Expand All @@ -67,6 +73,12 @@ object AvroModel {
val symbols: List<String>,
) : Type

@Serializable
data class UnionType(
val name: String,
val type: TypeList,
) : Type

@Serializable
data class LogicalType(
val type: String,
Expand Down Expand Up @@ -110,7 +122,9 @@ object AvroModel {
is SimpleType -> encoder.encodeSerializableValue(String.serializer(), value.value)
is RecordType -> encoder.encodeSerializableValue(RecordType.serializer(), value)
is ArrayType -> encoder.encodeSerializableValue(ArrayType.serializer(), value)
is MapType -> encoder.encodeSerializableValue(MapType.serializer(), value)
is EnumType -> encoder.encodeSerializableValue(EnumType.serializer(), value)
is UnionType -> encoder.encodeSerializableValue(UnionType.serializer(), value)
is LogicalType -> TODO()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ object AvroParser {
isNullable = false,
),
)

is AvroModel.ArrayType -> TODO()
is AvroModel.EnumType -> TODO()
is AvroModel.SimpleType -> TODO()
is AvroModel.LogicalType -> TODO()
is AvroModel.MapType -> TODO()
is AvroModel.UnionType -> TODO()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package community.flock.wirespec.converter.avro

import com.goncalossilva.resources.Resource
import community.flock.wirespec.compiler.core.ParseContext
import community.flock.wirespec.compiler.core.WirespecSpec
import community.flock.wirespec.compiler.core.parse
import community.flock.wirespec.compiler.core.parse.AST
import community.flock.wirespec.compiler.utils.noLogger
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.test.Test
Expand All @@ -14,6 +19,98 @@ class AvroEmitterTest {
ignoreUnknownKeys = true
}

fun parse(source: String): AST = object : ParseContext {
override val spec = WirespecSpec
override val logger = noLogger
}.parse(source).getOrNull() ?: error("Parsing failed.")

@Test
fun testTodoWs() {
val text = Resource("src/commonTest/resources/todo.ws")
.apply { assertTrue(exists()) }
.run { readText() }

val ast = parse(text)
val actual = AvroEmitter.emit(ast).let { json.encodeToString(it) }
val expected = """
[
{
"type": "enum",
"name": "Status",
"symbols": [
"PUBLIC",
"PRIVATE"
]
},
{
"type": "record",
"name": "Left",
"fields": [
{
"name": "left",
"type": "string"
}
]
},
{
"type": "record",
"name": "Right",
"fields": [
{
"name": "right",
"type": "string"
}
]
},
{
"name": "Either",
"type": [
"Left",
"Right"
]
},
{
"type": "record",
"name": "Todo",
"fields": [
{
"name": "id",
"type": "string"
},
{
"name": "name",
"type": [
"null",
"string"
]
},
{
"name": "done",
"type": "boolean"
},
{
"name": "tags",
"type": {
"type": "array",
"items": "string"
}
},
{
"name": "status",
"type": "Status"
},
{
"name": "either",
"type": "Either"
}
]
}
]
""".trimIndent()

assertEquals(expected, actual)
}

@Test
fun testSimple() {
val text = Resource("src/commonTest/resources/example.avsc")
Expand Down Expand Up @@ -315,7 +412,5 @@ class AvroEmitterTest {
}
]
""".trimIndent()

assertEquals(expected, actual)
}
}
27 changes: 27 additions & 0 deletions src/converter/avro/src/commonTest/resources/todo.ws
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type TodoId /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/g

enum Status {
PUBLIC,
PRIVATE
}

type Left {
left: String
}

type Right {
right: String
}

type Either = Left | Right

type Todo {
id: TodoId,
name: String?,
done: Boolean,
tags: String[],
status: Status,
either: Either
}

channel TodoChannel -> Todo
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@ object Utils {
else -> "<<<<<$value>>>>>"
},
)

is AvroModel.MapType -> TODO()
is AvroModel.UnionType -> TODO()
}
}
Loading

0 comments on commit d63e261

Please sign in to comment.