Skip to content
16 changes: 16 additions & 0 deletions library/src/main/kotlin/jp/co/panpanini/EnumGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class EnumGenerator {
typeSpec.addProperty(PropertySpec.builder("value", Int::class).initializer("value").build())
typeSpec.addType(companion.build())
typeSpec.addFunction(createToStringFunction())
typeSpec.addFunction(createToJsonFunction(type.values))
return typeSpec.build()
}

Expand Down Expand Up @@ -79,4 +80,19 @@ class EnumGenerator {
.addCode("return name")
.build()
}

private fun createToJsonFunction(values: List<File.Type.Enum.Value>): FunSpec {
val whenBlock = CodeBlock.builder()
.beginControlFlow("return when(this)")
values.forEach {
whenBlock.addStatement("%L -> %S", it.kotlinValueName, it.name)
}
whenBlock.addStatement("else -> %S", values.first().name)
whenBlock.endControlFlow()
return FunSpec.builder("toJson")
.addModifiers(KModifier.OVERRIDE)
.returns(String::class)
.addCode(whenBlock.build())
.build()
}
}
98 changes: 98 additions & 0 deletions library/src/main/kotlin/jp/co/panpanini/MessageGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ class MessageGenerator(private val file: File, private val kotlinTypeMappings: M
typeSpec.primaryConstructor(constructor.build())
typeSpec.addFunction(createSecondaryConstructor(type))

typeSpec.addFunction(createToJsonFunction(type))

mapEntry?.let {
typeSpec.addSuperinterface(mapEntry)
}


type.nestedTypes.map {
it.toTypeSpec(file, kotlinTypeMappings)
}.forEach {
Expand Down Expand Up @@ -517,6 +520,101 @@ class MessageGenerator(private val file: File, private val kotlinTypeMappings: M
return typeSpec.build()
}

private fun createToJsonFunction(type: File.Type.Message): FunSpec {
val builder = StringBuilder()
.append("return ")
.append("\"\"\"\n{ ")
.append(
type.fields.joinToString(", ") { field ->
"\n\"${field.name}\"·:·${getJsonValue(field)}"
}
)
.append("\n}\n\"\"\".trimIndent()")

return FunSpec.builder("toJson")
.addModifiers(KModifier.OVERRIDE)
.addCode(CodeBlock.of(builder.toString()))
.build()
}

private fun getJsonValue(field: File.Field, prefix: String = ""): String {
return when (field) {
is File.Field.Standard -> {
when {
field.map -> {
getMapJsonValue(field)
}
field.repeated -> {
getListJsonValue(field)
}
else -> {
"\"\${${getStandardJsonValue(field, "$prefix${field.kotlinFieldName}")}}\""

}
}
}
is File.Field.OneOf -> {
val block = CodeBlock.builder()
block.add("\${ ")
block.beginControlFlow("when (${field.kotlinFieldName})")
field.fields.forEach {
val code = CodeBlock.builder()
.beginControlFlow("is ${field.kotlinTypeName}.${it.kotlinFieldName.beginWithUpperCase()} ->")
.addStatement(
getJsonValue(it, "${field.kotlinFieldName}.")
)
.endControlFlow()
block.add(code.build())
}
block.beginControlFlow("is ${field.kotlinTypeName}.NotSet ->")
.addStatement("null")
.endControlFlow()

block.endControlFlow()
block.addStatement("}")
block.build().toString()
}
}
}

private fun getStandardJsonValue(field: File.Field.Standard, fieldName: String): String {
return when (field.type) {
File.Field.Type.BYTES -> "$fieldName.base64Encode()"
File.Field.Type.ENUM -> "$fieldName.toJson()"
File.Field.Type.MESSAGE -> "$fieldName.toJson()"
File.Field.Type.STRING -> fieldName
// TODO: INT64 -> return String value
else -> "$fieldName.toString()"
}
}

private fun getMapJsonValue(field: File.Field.Standard): String {
// get the value type so we can call the correct function to get the correct json representation
val value = field.mapEntry()?.fields?.get(1) as? File.Field.Standard
?: throw IllegalStateException("map value must not be null")

val builder = StringBuilder()
.append("{·")
.append("\${${field.kotlinFieldName}.entries.joinToString(\",·\")·{·(k,·v)·->\"\$k·:·\${${getStandardJsonValue(value, "v")}}\"·}·}")
.append("·}")

return builder.toString()


}

private fun getListJsonValue(field: File.Field.Standard): String {
val builder = StringBuilder()
builder.append("[ ")
builder.append(
"\${ ${field.kotlinFieldName}.joinToString(\", \") { ${getStandardJsonValue(field, "it")} }"
)
builder.append(" }")

builder.append(" ]")
return builder.toString()
}

private fun File.Field.Standard.sizeExpression(): CodeBlock {
val sizer = Sizer::class
val codeBlock = CodeBlock.builder()
Expand Down
Binary file modified library/src/test/resources/input/mappy.input
Binary file not shown.
5 changes: 5 additions & 0 deletions library/src/test/resources/kotlin/Item.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ data class Item(@JvmField val id: String = "", val unknownFields: Map<Int, Unkno

constructor(id: String) : this(id, emptyMap())

override fun toJson() = """
{
"id" : "${id}"
}
""".trimIndent()
fun Item.protoSizeImpl(): Int {
var protoSize = 0
if (id != DEFAULT_ID) {
Expand Down
9 changes: 9 additions & 0 deletions library/src/test/resources/kotlin/Language.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ enum class Language(override val value: Int) : Serializable, Message.Enum {
GO(4);

override fun toString(): String = name
override fun toJson(): String = when(this) {
PROTOBUF -> "PROTOBUF"
KOTLIN -> "KOTLIN"
JAVA -> "JAVA"
SWIFT -> "SWIFT"
GO -> "GO"
else -> "PROTOBUF"
}

companion object : Message.Enum.Companion<Language> {
@JvmStatic
override fun fromValue(value: Int): Language = when(value) {
Expand Down
Loading