Skip to content

Commit

Permalink
Emit compiler flags around Codable, Equatable, Hashable, and Redactable
Browse files Browse the repository at this point in the history
This allows you to turn off features for a module which are not needed to save binary size.
  • Loading branch information
JakeWharton committed Aug 19, 2020
1 parent 72c5e30 commit 2964424
Show file tree
Hide file tree
Showing 33 changed files with 539 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.outfoxx.swiftpoet.DeclaredTypeName
import io.outfoxx.swiftpoet.EnumerationCaseSpec
import io.outfoxx.swiftpoet.ExtensionSpec
import io.outfoxx.swiftpoet.FLOAT
import io.outfoxx.swiftpoet.FileMemberSpec
import io.outfoxx.swiftpoet.FileSpec
import io.outfoxx.swiftpoet.FunctionSpec
import io.outfoxx.swiftpoet.INT32
Expand Down Expand Up @@ -103,20 +104,20 @@ class SwiftGenerator private constructor(
}

fun generateTypeTo(type: Type, builder: FileSpec.Builder) {
val extensions = mutableListOf<ExtensionSpec>()
generateType(type, extensions).forEach {
val fileMembers = mutableListOf<FileMemberSpec>()
generateType(type, fileMembers).forEach {
builder.addType(it)
}

for (extension in extensions) {
builder.addExtension(extension)
for (extension in fileMembers) {
builder.addMember(extension)
}
}

private fun generateType(type: Type, extensions: MutableList<ExtensionSpec>) = when (type) {
is MessageType -> generateMessage(type, extensions)
is EnumType -> listOf(generateEnum(type, extensions))
is EnclosingType -> listOf(generateEnclosing(type, extensions))
private fun generateType(type: Type, fileMembers: MutableList<FileMemberSpec>) = when (type) {
is MessageType -> generateMessage(type, fileMembers)
is EnumType -> listOf(generateEnum(type, fileMembers))
is EnclosingType -> listOf(generateEnclosing(type, fileMembers))
else -> error("Unknown type $type")
}

Expand All @@ -143,7 +144,10 @@ class SwiftGenerator private constructor(
private val MessageType.isHeapAllocated get() = fields.size + oneOfs.size >= 16

@OptIn(ExperimentalStdlibApi::class) // TODO move to build flag
private fun generateMessage(type: MessageType, extensions: MutableList<ExtensionSpec>): List<TypeSpec> {
private fun generateMessage(
type: MessageType,
fileMembers: MutableList<FileMemberSpec>
): List<TypeSpec> {
val structType = type.typeName
val oneOfEnumNames = type.oneOfs.associateWith { structType.nestedType(it.name.capitalize(US)) }

Expand Down Expand Up @@ -193,18 +197,29 @@ class SwiftGenerator private constructor(
generateMessageConstructor(type, oneOfEnumNames)
}

generateMessageOneOfs(type, oneOfEnumNames, extensions)
generateMessageOneOfs(type, oneOfEnumNames, fileMembers)

type.nestedTypes.forEach { nestedType ->
generateType(nestedType, extensions).forEach {
generateType(nestedType, fileMembers).forEach {
addType(it)
}
}
}
.build()

extensions += ExtensionSpec.builder(structType).addSuperType(equatable).build()
extensions += ExtensionSpec.builder(structType).addSuperType(hashable).build()
val structEquatableExtension = ExtensionSpec.builder(structType)
.addSuperType(equatable)
.build()
fileMembers += FileMemberSpec.builder(structEquatableExtension)
.addGuard("!$FLAG_REMOVE_EQUATABLE")
.build()

val structHashableExtension = ExtensionSpec.builder(structType)
.addSuperType(hashable)
.build()
fileMembers += FileMemberSpec.builder(structHashableExtension)
.addGuard("!$FLAG_REMOVE_HASHABLE")
.build()

val redactionExtension = if (type.fields.any { it.isRedacted }) {
ExtensionSpec.builder(structType)
Expand Down Expand Up @@ -234,8 +249,8 @@ class SwiftGenerator private constructor(
}
.build()

extensions += ExtensionSpec.builder(structType)
.addSuperType(type.codeableType)
val structProtoCodableExtension = ExtensionSpec.builder(structType)
.addSuperType(type.protoCodableType)
.addFunction(FunctionSpec.constructorBuilder()
.addModifiers(PUBLIC)
.addParameter("from", "reader", protoReader)
Expand All @@ -249,15 +264,38 @@ class SwiftGenerator private constructor(
.addStatement("try %N.encode(to: writer)", storageName)
.build())
.build()
extensions += ExtensionSpec.builder(storageType)
fileMembers += FileMemberSpec.builder(structProtoCodableExtension).build()

val storageProtoCodableExtension = ExtensionSpec.builder(storageType)
.messageProtoCodableExtension(type, structType, oneOfEnumNames, propertyNames)
.build()
fileMembers += FileMemberSpec.builder(storageProtoCodableExtension).build()

extensions += ExtensionSpec.builder(structType).addSuperType(codable).build()
extensions += messageCodableExtension(type, storageType)
val structCodableExtension = ExtensionSpec.builder(structType)
.addSuperType(codable)
.build()
fileMembers += FileMemberSpec.builder(structCodableExtension)
.addGuard("!$FLAG_REMOVE_CODABLE")
.build()

val storageCodableExtension = messageCodableExtension(type, storageType)
fileMembers += FileMemberSpec.builder(storageCodableExtension)
.addGuard("!$FLAG_REMOVE_CODABLE")
.build()

val storageEquatableExtension = ExtensionSpec.builder(storageType)
.addSuperType(equatable)
.build()
fileMembers += FileMemberSpec.builder(storageEquatableExtension)
.addGuard("!$FLAG_REMOVE_EQUATABLE")
.build()

extensions += ExtensionSpec.builder(storageType).addSuperType(equatable).build()
extensions += ExtensionSpec.builder(storageType).addSuperType(hashable).build()
val storageHashableExtension = ExtensionSpec.builder(storageType)
.addSuperType(hashable)
.build()
fileMembers += FileMemberSpec.builder(storageHashableExtension)
.addGuard("!$FLAG_REMOVE_HASHABLE")
.build()

if (redactionExtension != null) {
redactionExtension.addProperty(PropertySpec.varBuilder("description", STRING)
Expand All @@ -267,22 +305,31 @@ class SwiftGenerator private constructor(
.build())
.build())

extensions += ExtensionSpec.builder(storageType)
val storageRedactableExtension = ExtensionSpec.builder(storageType)
.addSuperType(redactable)
.addTypeAlias(TypeAliasSpec.builder("RedactedKeys", structType.nestedType("RedactedKeys"))
.build())
.addTypeAlias(
TypeAliasSpec.builder("RedactedKeys", structType.nestedType("RedactedKeys"))
.build())
.build()
fileMembers += FileMemberSpec.builder(storageRedactableExtension)
.addGuard("!$FLAG_REMOVE_REDACTABLE")
.build()
}
} else {
extensions += ExtensionSpec.builder(structType)
val protoCodableExtension = ExtensionSpec.builder(structType)
.messageProtoCodableExtension(type, structType, oneOfEnumNames, propertyNames)
.build()
fileMembers += FileMemberSpec.builder(protoCodableExtension).build()

extensions += messageCodableExtension(type, structType)
fileMembers += FileMemberSpec.builder(messageCodableExtension(type, structType))
.addGuard("!$FLAG_REMOVE_CODABLE")
.build()
}

if (redactionExtension != null) {
extensions += redactionExtension.build()
fileMembers += FileMemberSpec.builder(redactionExtension.build())
.addGuard("!$FLAG_REMOVE_REDACTABLE")
.build()
}

return typeSpecs
Expand All @@ -294,7 +341,7 @@ class SwiftGenerator private constructor(
oneOfEnumNames: Map<OneOf, DeclaredTypeName>,
propertyNames: Collection<String>
): ExtensionSpec.Builder = apply {
addSuperType(type.codeableType)
addSuperType(type.protoCodableType)

val reader = if ("reader" in propertyNames) "_reader" else "reader"
val token = if ("token" in propertyNames) "_token" else "token"
Expand Down Expand Up @@ -428,7 +475,7 @@ class SwiftGenerator private constructor(
.build())
}

private val MessageType.codeableType: DeclaredTypeName
private val MessageType.protoCodableType: DeclaredTypeName
get() = when (syntax) {
PROTO_2 -> proto2Codable
PROTO_3 -> proto3Codable
Expand Down Expand Up @@ -673,7 +720,7 @@ class SwiftGenerator private constructor(
private fun TypeSpec.Builder.generateMessageOneOfs(
type: MessageType,
oneOfEnumNames: Map<OneOf, DeclaredTypeName>,
extensions: MutableList<ExtensionSpec>
fileMembers: MutableList<FileMemberSpec>
) {
type.oneOfs.forEach { oneOf ->
val enumName = oneOfEnumNames.getValue(oneOf)
Expand Down Expand Up @@ -712,15 +759,22 @@ class SwiftGenerator private constructor(
.build())
.build())

extensions += ExtensionSpec.builder(enumName)
val equatableExtension = ExtensionSpec.builder(enumName)
.addSuperType(equatable)
.build()
extensions += ExtensionSpec.builder(enumName)
fileMembers += FileMemberSpec.builder(equatableExtension)
.addGuard("!$FLAG_REMOVE_EQUATABLE")
.build()

val hashableExtension = ExtensionSpec.builder(enumName)
.addSuperType(hashable)
.build()
fileMembers += FileMemberSpec.builder(hashableExtension)
.addGuard("!$FLAG_REMOVE_HASHABLE")
.build()

if (oneOf.fields.any { it.isRedacted }) {
extensions += ExtensionSpec.builder(enumName)
val redactableExtension = ExtensionSpec.builder(enumName)
.addSuperType(redactable)
.addType(TypeSpec.enumBuilder("RedactedKeys")
.addModifiers(PUBLIC)
Expand All @@ -735,6 +789,9 @@ class SwiftGenerator private constructor(
}
.build())
.build()
fileMembers += FileMemberSpec.builder(redactableExtension)
.addGuard("!$FLAG_REMOVE_REDACTABLE")
.build()
}
}
}
Expand All @@ -761,7 +818,10 @@ class SwiftGenerator private constructor(
return false
}

private fun generateEnum(type: EnumType, extensions: MutableList<ExtensionSpec>): TypeSpec {
private fun generateEnum(
type: EnumType,
fileMembers: MutableList<FileMemberSpec>
): TypeSpec {
val enumName = type.typeName
return TypeSpec.enumBuilder(enumName)
.addModifiers(PUBLIC)
Expand Down Expand Up @@ -796,23 +856,26 @@ class SwiftGenerator private constructor(
.build())
}
type.nestedTypes.forEach { nestedType ->
generateType(nestedType, extensions).forEach {
generateType(nestedType, fileMembers).forEach {
addType(it)
}
}
}
.build()
}

private fun generateEnclosing(type: EnclosingType, extensions: MutableList<ExtensionSpec>): TypeSpec {
private fun generateEnclosing(
type: EnclosingType,
fileMembers: MutableList<FileMemberSpec>
): TypeSpec {
return TypeSpec.enumBuilder(type.typeName)
.addModifiers(PUBLIC)
.addKdoc("%N\n",
"*Note:* This type only exists to maintain class structure for its nested types and " +
"is not an actual message.")
.apply {
type.nestedTypes.forEach { nestedType ->
generateType(nestedType, extensions).forEach {
generateType(nestedType, fileMembers).forEach {
addType(it)
}
}
Expand Down Expand Up @@ -843,6 +906,11 @@ class SwiftGenerator private constructor(
// Options.ENUM_OPTIONS to ClassName("com.google.protobuf", "EnumOptions")
)

private const val FLAG_REMOVE_CODABLE = "WIRE_REMOVE_CODABLE"
private const val FLAG_REMOVE_EQUATABLE = "WIRE_REMOVE_EQUATABLE"
private const val FLAG_REMOVE_HASHABLE = "WIRE_REMOVE_HASHABLE"
private const val FLAG_REMOVE_REDACTABLE = "WIRE_REMOVE_REDACTABLE"

@JvmStatic @JvmName("get")
operator fun invoke(
schema: Schema,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.outfoxx.swiftpoet

class FileMemberSpec internal constructor(builder: Builder) {
val kdoc = builder.kdoc.build()
val member = builder.member
val guardTest = builder.guardTest.build()

internal fun emit(out: CodeWriter): CodeWriter {

out.emitKdoc(kdoc)

if (guardTest.isNotEmpty()) {
out.emit("#if ")
out.emitCode(guardTest)
out.emit("\n")
}

when (member) {
is TypeSpec -> member.emit(out)
is FunctionSpec -> member.emit(out, null, setOf(Modifier.PUBLIC))
is PropertySpec -> member.emit(out, setOf(Modifier.PUBLIC))
is TypeAliasSpec -> member.emit(out)
is ExtensionSpec -> member.emit(out)
else -> throw AssertionError()
}

if (guardTest.isNotEmpty()) {
out.emit("#endif")
out.emit("\n")
}

return out
}

class Builder internal constructor(internal val member: Any) {
internal val kdoc = CodeBlock.builder()
internal val guardTest = CodeBlock.builder()

fun addKdoc(format: String, vararg args: Any) = apply {
kdoc.add(format, *args)
}

fun addKdoc(block: CodeBlock) = apply {
kdoc.add(block)
}

fun addGuard(test: CodeBlock) = apply {
guardTest.add(test)
}

fun addGuard(format: String, vararg args: Any) = apply {
addGuard(CodeBlock.of(format, args))
}

fun build(): FileMemberSpec {
return FileMemberSpec(this)
}
}

companion object {
@JvmStatic fun builder(member: TypeSpec) = Builder(member)

@JvmStatic fun builder(member: FunctionSpec) = Builder(member)

@JvmStatic fun builder(member: PropertySpec) = Builder(member)

@JvmStatic fun builder(member: TypeAliasSpec) = Builder(member)

@JvmStatic fun builder(member: ExtensionSpec) = Builder(member)
}

}
Loading

0 comments on commit 2964424

Please sign in to comment.