Skip to content

Commit

Permalink
Make Ktlint happy
Browse files Browse the repository at this point in the history
  • Loading branch information
serras committed Dec 20, 2023
1 parent 60839f7 commit c230007
Show file tree
Hide file tree
Showing 46 changed files with 772 additions and 578 deletions.
78 changes: 44 additions & 34 deletions kopykat-ksp/src/main/kotlin/at/kopyk/CopyConstructors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,35 @@ import com.squareup.kotlinpoet.ksp.toKModifier
internal data class CopyPair(
val self: TypeCompileScope,
val from: KSClassDeclaration,
val to: KSClassDeclaration
val to: KSClassDeclaration,
)

internal val ClassCompileScope.allCopies: Sequence<CopyPair>
get() = sequence {
val copyFromTargets = typesFor<CopyFrom>() + typesFor<Copy>()
val copyToTargets = typesFor<CopyTo>() + typesFor<Copy>()

val self = this@allCopies
fun pair(from: KSClassDeclaration, to: KSClassDeclaration) = CopyPair(self, from, to)
yieldAll(copyFromTargets.map { other -> pair(other, self) })
yieldAll(copyToTargets.map { other -> pair(self, other) })
}
get() =
sequence {
val copyFromTargets = typesFor<CopyFrom>() + typesFor<Copy>()
val copyToTargets = typesFor<CopyTo>() + typesFor<Copy>()

val self = this@allCopies

fun pair(
from: KSClassDeclaration,
to: KSClassDeclaration,
) = CopyPair(self, from, to)
yieldAll(copyFromTargets.map { other -> pair(other, self) })
yieldAll(copyToTargets.map { other -> pair(self, other) })
}

internal val CopyPair.name get() = nameFor(from, to)

internal fun nameFor(from: KSClassDeclaration, to: KSClassDeclaration) =
from.className.append(to.baseName).reflectionName()
internal fun nameFor(
from: KSClassDeclaration,
to: KSClassDeclaration,
) = from.className.append(to.baseName).reflectionName()

internal fun fileSpec(
others: Sequence<CopyPair>,
copyPair: CopyPair
copyPair: CopyPair,
): FileSpec? =
with(copyPair) {
when {
Expand All @@ -55,7 +62,7 @@ internal fun fileSpec(

private fun TypeCompileScope.copyConstructorFileSpec(
copyPair: CopyPair,
others: Sequence<CopyPair>
others: Sequence<CopyPair>,
): FileSpec =
with(copyPair) {
buildFile(fileName = name) {
Expand All @@ -64,26 +71,26 @@ private fun TypeCompileScope.copyConstructorFileSpec(
addParameter(name = "from", type = from.className)
val visibilities = listOf(from.getVisibility()) + from.getAllProperties().map { it.getVisibility() }
visibilities.minimal().toKModifier()?.let { addModifiers(it) }
val propertyAssignments = to.properties.toList().zipByName(from.getAllProperties().toList()) { to, from ->
propertyDefinition(others, from, to)
}.filterNotNull()
val propertyAssignments =
to.properties.toList().zipByName(from.getAllProperties().toList()) { to, from ->
propertyDefinition(others, from, to)
}.filterNotNull()
addReturn("${to.baseName}(${propertyAssignments.joinToString()})")
}
}
}

private fun TypeCompileScope.reportMismatchedProperties(
copyPair: CopyPair
): Nothing? = with(copyPair) {
val message = "${to.fullName} must have the same constructor properties as ${from.fullName}"
logger.error(message = message, symbol = self)
null
}
private fun TypeCompileScope.reportMismatchedProperties(copyPair: CopyPair): Nothing? =
with(copyPair) {
val message = "${to.fullName} must have the same constructor properties as ${from.fullName}"
logger.error(message = message, symbol = self)
null
}

private fun propertyDefinition(
others: Sequence<CopyPair>,
from: KSPropertyDeclaration,
to: KSPropertyDeclaration
to: KSPropertyDeclaration,
): String? {
val fromType = from.typeDeclaration
val toType = to.typeDeclaration
Expand All @@ -100,7 +107,7 @@ private val KSPropertyDeclaration.typeDeclaration

private fun KSClassDeclaration.isIsomorphicOf(
copies: Sequence<CopyPair>,
other: KSClassDeclaration
other: KSClassDeclaration,
): Boolean {
val properties = getAllProperties().toSet()
val otherProperties = other.getPrimaryConstructorProperties().toSet()
Expand All @@ -116,7 +123,7 @@ private val Iterable<KSPropertyDeclaration>.names get() = map { it.baseName }

private fun KSPropertyDeclaration.isCopiableTo(
copies: Sequence<CopyPair>,
other: KSPropertyDeclaration
other: KSPropertyDeclaration,
): Boolean {
val thisDecl = typeDeclaration
val otherDecl = other.typeDeclaration
Expand All @@ -126,10 +133,12 @@ private fun KSPropertyDeclaration.isCopiableTo(
}
}

private fun KSPropertyDeclaration.isAssignableFrom(other: KSPropertyDeclaration) =
type.resolve().isAssignableFrom(other.type.resolve())
private fun KSPropertyDeclaration.isAssignableFrom(other: KSPropertyDeclaration) = type.resolve().isAssignableFrom(other.type.resolve())

private fun Sequence<CopyPair>.hasCopyConstructor(from: KSClassDeclaration, to: KSClassDeclaration): Boolean {
private fun Sequence<CopyPair>.hasCopyConstructor(
from: KSClassDeclaration,
to: KSClassDeclaration,
): Boolean {
val name = nameFor(from, to)
return any { it.name == name }
}
Expand All @@ -148,11 +157,12 @@ private inline fun <reified T : Annotation> KSAnnotated.annotationsOf(): Sequenc

private inline fun <A> Iterable<KSPropertyDeclaration>.zipByName(
other: Iterable<KSPropertyDeclaration>,
transform: (KSPropertyDeclaration, KSPropertyDeclaration) -> A
transform: (KSPropertyDeclaration, KSPropertyDeclaration) -> A,
): List<A> =
map { thisProp ->
val otherProp = other.first { otherProp ->
thisProp.baseName == otherProp.baseName
}
val otherProp =
other.first { otherProp ->
thisProp.baseName == otherProp.baseName
}
transform(thisProp, otherProp)
}
48 changes: 25 additions & 23 deletions kopykat-ksp/src/main/kotlin/at/kopyk/CopyMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,32 @@ import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.ksp.toKModifier

internal val TypeCompileScope.copyMapFunctionKt: FileSpec
get() = buildFile(fileName = target.append("CopyMap").reflectionName()) {
val parameterized = target.parameterized
addGeneratedMarker()
addInlinedFunction(name = "copyMap", receives = parameterized, returns = parameterized) {
visibility.toKModifier()?.let { addModifiers(it) }
properties
.onEachRun {
addParameter(
name = baseName,
type = typeName.asTransformLambda(receiver = parameterized),
defaultValue = "{ it }"
)
}
.mapRun { "$baseName = $baseName(this, this.$baseName)" }
.run { addReturn(repeatOnSubclasses(joinToString(), "copy")) }
get() =
buildFile(fileName = target.append("CopyMap").reflectionName()) {
val parameterized = target.parameterized
addGeneratedMarker()
addInlinedFunction(name = "copyMap", receives = parameterized, returns = parameterized) {
visibility.toKModifier()?.let { addModifiers(it) }
properties
.onEachRun {
addParameter(
name = baseName,
type = typeName.asTransformLambda(receiver = parameterized),
defaultValue = "{ it }",
)
}
.mapRun { "$baseName = $baseName(this, this.$baseName)" }
.run { addReturn(repeatOnSubclasses(joinToString(), "copy")) }
}
}
}

private fun TypeCompileScope.repeatOnSubclasses(
line: String,
functionName: String
): String = when (typeCategory) {
Value -> "$fullName($line)"
Data -> "$functionName($line)"
Sealed -> sealedTypes.joinWithWhen { "is ${it.fullName} -> $functionName($line)" }
else -> error("Unknown type category for ${target.canonicalName}")
}
functionName: String,
): String =
when (typeCategory) {
Value -> "$fullName($line)"
Data -> "$functionName($line)"
Sealed -> sealedTypes.joinWithWhen { "is ${it.fullName} -> $functionName($line)" }
else -> error("Unknown type category for ${target.canonicalName}")
}
57 changes: 30 additions & 27 deletions kopykat-ksp/src/main/kotlin/at/kopyk/FileCompileScope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import org.apache.commons.io.FilenameUtils

internal fun ProcessorScope.processFiles(
resolver: Resolver,
block: FileCompileScope.() -> Unit
block: FileCompileScope.() -> Unit,
) {
val files = resolver.getAllFiles()
if (files.none(KSFile::hasGeneratedMarker)) {
Expand All @@ -32,23 +32,25 @@ internal fun ProcessorScope.processFiles(

internal class FileCompileScope(
files: Sequence<KSFile>,
scope: ProcessorScope
scope: ProcessorScope,
) : LoggerScope by scope, OptionsScope by scope {

private val codegen: CodeGenerator = scope.codegen

val declarations = files
.flatMap { it.allNestedDeclarations() }
.onEach { it.isKnownWithCopyExtension() }
.onEach { it.checkRedundantAnnotation() }
.filter { it.typeCategory is Known }
val declarations =
files
.flatMap { it.allNestedDeclarations() }
.onEach { it.isKnownWithCopyExtension() }
.onEach { it.checkRedundantAnnotation() }
.filter { it.typeCategory is Known }

val typeAliases = declarations
.filterIsInstance<KSTypeAlias> { isKnownWithCopyExtension() }
val typeAliases =
declarations
.filterIsInstance<KSTypeAlias> { isKnownWithCopyExtension() }

val classes = declarations
.filterIsInstance<KSClassDeclaration>()
.filter { it.shouldGenerate() }
val classes =
declarations
.filterIsInstance<KSClassDeclaration>()
.filter { it.shouldGenerate() }

val KSClassDeclaration.classScope: ClassCompileScope
get() = ClassCompileScope(this, classes, logger)
Expand All @@ -65,7 +67,7 @@ internal class FileCompileScope(
'@CopyExtensions' may only be used in data or value classes,
sealed hierarchies of those, or type aliases of those.
""".trimIndent(),
this
this,
)
}
}
Expand All @@ -83,27 +85,28 @@ internal class FileCompileScope(
Add 'arg("annotatedOnly", "true")' to your KSP configuration to change this option.
More info at https://kopyk.at/#enable-only-for-selected-classes.
""".trimIndent(),
this
this,
)
}
}

@OptIn(KspExperimental::class)
private fun KSDeclaration.shouldGenerate(): Boolean = when (options.generate) {
is KopyKatGenerate.Error ->
false
private fun KSDeclaration.shouldGenerate(): Boolean =
when (options.generate) {
is KopyKatGenerate.Error ->
false

is KopyKatGenerate.All ->
true
is KopyKatGenerate.All ->
true

is KopyKatGenerate.Annotated ->
isAnnotationPresent(CopyExtensions::class)
is KopyKatGenerate.Annotated ->
isAnnotationPresent(CopyExtensions::class)

is KopyKatGenerate.Packages -> {
val pkg = packageName.asString()
options.generate.patterns.any { pattern ->
FilenameUtils.wildcardMatch(pkg, pattern)
is KopyKatGenerate.Packages -> {
val pkg = packageName.asString()
options.generate.patterns.any { pattern ->
FilenameUtils.wildcardMatch(pkg, pattern)
}
}
}
}
}
56 changes: 33 additions & 23 deletions kopykat-ksp/src/main/kotlin/at/kopyk/KopyKatOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,60 @@ package at.kopyk
import com.google.devtools.ksp.processing.KSPLogger

internal sealed interface KopyKatGenerate {
object Error : KopyKatGenerate
object Annotated : KopyKatGenerate
data object Error : KopyKatGenerate

data object Annotated : KopyKatGenerate

sealed interface NotAnnotated : KopyKatGenerate
object All : NotAnnotated

data object All : NotAnnotated

data class Packages(val patterns: List<String>) : NotAnnotated

companion object {
const val ALL = "all"
const val ANNOTATED = "annotated"
const val PACKAGES_PREFIX = "packages"

fun fromKspOptions(logger: KSPLogger, generate: String?): KopyKatGenerate = when {
generate == null -> All
generate == ALL -> All
generate == ANNOTATED -> Annotated
generate.startsWith(PACKAGES_PREFIX) ->
Packages(generate.split(':').drop(1))
else -> {
logger.error("Unrecognized value for 'generate'", null)
Error // return something, although the error is reported
fun fromKspOptions(
logger: KSPLogger,
generate: String?,
): KopyKatGenerate =
when {
generate == null -> All
generate == ALL -> All
generate == ANNOTATED -> Annotated
generate.startsWith(PACKAGES_PREFIX) ->
Packages(generate.split(':').drop(1))
else -> {
logger.error("Unrecognized value for 'generate'", null)
Error // return something, although the error is reported
}
}
}
}
}

internal data class KopyKatOptions(
val copyMap: Boolean,
val mutableCopy: Boolean,
val hierarchyCopy: Boolean,
val generate: KopyKatGenerate
val generate: KopyKatGenerate,
)

internal fun KopyKatOptions(logger: KSPLogger, options: Map<String, String>) =
KopyKatOptions(
copyMap = options.parseBoolOrTrue(COPY_MAP),
mutableCopy = options.parseBoolOrTrue(MUTABLE_COPY),
hierarchyCopy = options.parseBoolOrTrue(HIERARCHY_COPY),
generate = KopyKatGenerate.fromKspOptions(logger, options[GENERATE])
)
@Suppress("FunctionName")
internal fun KopyKatOptions(
logger: KSPLogger,
options: Map<String, String>,
) = KopyKatOptions(
copyMap = options.parseBoolOrTrue(COPY_MAP),
mutableCopy = options.parseBoolOrTrue(MUTABLE_COPY),
hierarchyCopy = options.parseBoolOrTrue(HIERARCHY_COPY),
generate = KopyKatGenerate.fromKspOptions(logger, options[GENERATE]),
)

private const val COPY_MAP = "copyMap"
private const val MUTABLE_COPY = "mutableCopy"
private const val HIERARCHY_COPY = "hierarchyCopy"
private const val GENERATE = "generate"

private fun Map<String, String>.parseBoolOrTrue(key: String) =
this[key]?.lowercase()?.toBooleanStrictOrNull() ?: true
private fun Map<String, String>.parseBoolOrTrue(key: String) = this[key]?.lowercase()?.toBooleanStrictOrNull() ?: true

0 comments on commit c230007

Please sign in to comment.