Skip to content

Commit

Permalink
Add KopyFunctions to set the functions to be generated
Browse files Browse the repository at this point in the history
  • Loading branch information
JavierSegoviaCordoba committed Aug 25, 2024
1 parent 1cb47bb commit 74ecc4b
Show file tree
Hide file tree
Showing 20 changed files with 388 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- `KopyFunctions` to set the functions that will be generated

### Changed

### Deprecated
Expand Down
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ default value is `Auto`, which uses the same visibility the primary constructor

Possible values:

- `KopyVisibility.Auto`
- `KopyVisibility.Public`
- `KopyVisibility.Internal`
- `KopyVisibility.Protected`
- `KopyVisibility.Private`
- `KopyVisibility.Auto` (Default): The visibility of the primary constructor is used.
- `KopyVisibility.Public`: The visibility of the generated functions will be `public`.
- `KopyVisibility.Internal`: The visibility of the generated functions will be `internal`.
- `KopyVisibility.Protected`: The visibility of the generated functions will be `protected`.
- `KopyVisibility.Private`: The visibility of the generated functions will be `private`.

It is possible to have a more restrictive Kopy `copy` and `invoke` functions than the original one,
for example by providing the `KopyVisiblity.Private` and the primary constructor being `public` or
Expand All @@ -89,7 +89,31 @@ kopy {
}
```

### Example
#### Functions

The `functions` option allows to decide which functions will be generated.

Possible values:

- `KopyFunctions.All` (default): Both, `copy` and `invoke` functions, will be generated.
- `KopyFunctions.Copy`: Only the `copy` function will be generated.
- `KopyFunctions.Invoke`: Only the `invoke` function will be generated.

##### Example

```kotlin
import com.javiersc.kotlin.kopy.args.KopyFunctions

plugins {
id("com.javiersc.kotlin.kopy") version "$version"
}

kopy {
functions = KopyFunctions.All
}
```

### Kopy Example

```kotlin
import com.javiersc.kotlin.kopy.Kopy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.javiersc.kotlin.kopy.args

import java.io.Serializable

public enum class KopyFunctions(public val value: String) : Serializable {
All(value = "all"),
Copy(value = "copy"),
Invoke(value = "invoke"),
;

public companion object {

public const val NAME: String = "KopyFunctions"
public const val DESCRIPTION: String = "Kopy functions to be generated"

public fun from(value: String): KopyFunctions =
when (value) {
All.name -> All
All.value -> All
Copy.name -> Copy
Copy.value -> Copy
Invoke.name -> Invoke
Invoke.value -> Invoke
else -> All
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.javiersc.kotlin.kopy.args

import io.kotest.matchers.shouldBe
import kotlin.test.Test

class KopyFunctionsTest {

@Test
fun `kopy functions`() {
KopyFunctions.from("all") shouldBe KopyFunctions.All
KopyFunctions.from("All") shouldBe KopyFunctions.All
KopyFunctions.from("copy") shouldBe KopyFunctions.Copy
KopyFunctions.from("Copy") shouldBe KopyFunctions.Copy
KopyFunctions.from("invoke") shouldBe KopyFunctions.Invoke
KopyFunctions.from("Invoke") shouldBe KopyFunctions.Invoke
KopyFunctions.from("random") shouldBe KopyFunctions.All
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package com.javiersc.kotlin.kopy.compiler

import com.javiersc.kotlin.kopy.args.KopyFunctions
import com.javiersc.kotlin.kopy.args.KopyVisibility
import com.javiersc.kotlin.kopy.compiler.KopyCompilerProjectData.Group
import com.javiersc.kotlin.kopy.compiler.KopyCompilerProjectData.Name
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey

public class KopyCommandLineProcessor : CommandLineProcessor {

override val pluginId: String = "$Group.$Name"

override val pluginOptions: Collection<AbstractCliOption> =
listOf(
CliOption(
optionName = KopyFunctions.NAME,
valueDescription = KopyFunctions.DESCRIPTION,
description = KopyFunctions.DESCRIPTION,
required = true,
),
CliOption(
optionName = KopyVisibility.NAME,
valueDescription = KopyVisibility.DESCRIPTION,
Expand All @@ -27,7 +35,12 @@ public class KopyCommandLineProcessor : CommandLineProcessor {
value: String,
configuration: CompilerConfiguration
) {
val kopyVisibility: KopyVisibility = KopyVisibility.from(value)
configuration.put(KopyKey.Visibility, kopyVisibility)
fun <T : Any> put(key: CompilerConfigurationKey<T>, value: T) =
configuration.put(key, value)

when (option.optionName) {
KopyFunctions.NAME -> put(KopyKey.Functions, KopyFunctions.from(value))
KopyVisibility.NAME -> put(KopyKey.Visibility, KopyVisibility.from(value))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.javiersc.kotlin.kopy.compiler

import com.javiersc.kotlin.kopy.args.KopyFunctions
import com.javiersc.kotlin.kopy.args.KopyVisibility
import org.jetbrains.kotlin.config.CompilerConfigurationKey

internal object KopyKey {
val Visibility = CompilerConfigurationKey<KopyVisibility>(KopyVisibility.NAME)
val Functions = CompilerConfigurationKey<KopyFunctions>(KopyFunctions.NAME)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.javiersc.kotlin.kopy.KopyFunctionInvoke
import com.javiersc.kotlin.kopy.KopyFunctionSet
import com.javiersc.kotlin.kopy.KopyFunctionUpdate
import com.javiersc.kotlin.kopy.KopyFunctionUpdateEach
import com.javiersc.kotlin.kopy.args.KopyFunctions
import com.javiersc.kotlin.kopy.args.KopyVisibility
import com.javiersc.kotlin.kopy.compiler.KopyKey
import com.javiersc.kotlin.kopy.compiler.fir.Key
Expand Down Expand Up @@ -67,6 +68,9 @@ internal class FirKopyDeclarationGenerationExtension(
private val kopyVisibility: KopyVisibility
get() = configuration.get(KopyKey.Visibility, KopyVisibility.Auto)

private val kopyFunctions: KopyFunctions
get() = configuration.get(KopyKey.Functions, KopyFunctions.All)

private val kopyOptInClassId: ClassId = "com.javiersc.kotlin.kopy.KopyOptIn".toClassId()
private val kopyFunctionCopyClassId: ClassId = classId<KopyFunctionCopy>()
private val kopyFunctionInvokeClassId: ClassId = classId<KopyFunctionInvoke>()
Expand Down Expand Up @@ -144,11 +148,15 @@ internal class FirKopyDeclarationGenerationExtension(
if (!owner.hasAnnotation(classId<Kopy>(), session)) return@buildList
if (!owner.isData) return@buildList

val copyFunction: FirNamedFunctionSymbol? = createCopyFun(callableId, owner)
if (copyFunction != null) add(copyFunction)
if (kopyFunctions == KopyFunctions.All || kopyFunctions == KopyFunctions.Copy) {
val copyFunction: FirNamedFunctionSymbol? = createCopyFun(callableId, owner)
if (copyFunction != null) add(copyFunction)
}

val invokeFunction: FirNamedFunctionSymbol? = createInvokeFun(callableId, owner)
if (invokeFunction != null) add(invokeFunction)
if (kopyFunctions == KopyFunctions.All || kopyFunctions == KopyFunctions.Invoke) {
val invokeFunction: FirNamedFunctionSymbol? = createInvokeFun(callableId, owner)
if (invokeFunction != null) add(invokeFunction)
}

val setFunction: FirNamedFunctionSymbol? = createSetFun(callableId, owner)
if (setFunction != null) add(setFunction)
Expand Down
39 changes: 39 additions & 0 deletions kopy-compiler/test-data/diagnostics-kopy-functions/all/all.fir.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FILE: all.kt
package com.javiersc.kotlin.kopy.playground

@R|com/javiersc/kotlin/kopy/Kopy|() public final data class Foo : R|kotlin/Any| {
public constructor(number: R|kotlin/Int|, letter: R|kotlin/Char|): R|com/javiersc/kotlin/kopy/playground/Foo| {
super<R|kotlin/Any|>()
}

public final val number: R|kotlin/Int| = R|<local>/number|
public get(): R|kotlin/Int|

public final val letter: R|kotlin/Char| = R|<local>/letter|
public get(): R|kotlin/Char|

public final operator fun component1(): R|kotlin/Int|

public final operator fun component2(): R|kotlin/Char|

public final fun copy(number: R|kotlin/Int| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.number|, letter: R|kotlin/Char| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.letter|): R|com/javiersc/kotlin/kopy/playground/Foo|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdate|() public final infix fun <U> R|U|.update(transform: R|(U) -> U|): R|kotlin/Unit| {
}

public final val _atomic: R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|
public get(): R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionSet|() public final infix fun <S> R|S|.set(other: R|S|): R|kotlin/Unit| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionCopy|() public final infix fun copy(copy: R|com/javiersc/kotlin/kopy/playground/Foo.() -> kotlin/Unit|): R|com/javiersc/kotlin/kopy/playground/Foo| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdateEach|() public final infix fun <UE> R|kotlin/collections/Iterable<UE>|.updateEach(transform: R|(UE) -> UE|): R|kotlin/Unit| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionInvoke|() public final operator infix fun invoke(copy: R|com/javiersc/kotlin/kopy/playground/Foo.() -> kotlin/Unit|): R|com/javiersc/kotlin/kopy/playground/Foo| {
}

}
9 changes: 9 additions & 0 deletions kopy-compiler/test-data/diagnostics-kopy-functions/all/all.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// OPT_IN: com.javiersc.kotlin.kopy.KopyOptIn
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_CLASS -MISSING_DEPENDENCY_SUPERCLASS

package com.javiersc.kotlin.kopy.playground

import com.javiersc.kotlin.kopy.Kopy

@Kopy
data class Foo(val number: Int, val letter: Char)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FILE: copy.kt
package com.javiersc.kotlin.kopy.playground

@R|com/javiersc/kotlin/kopy/Kopy|() public final data class Foo : R|kotlin/Any| {
public constructor(number: R|kotlin/Int|, letter: R|kotlin/Char|): R|com/javiersc/kotlin/kopy/playground/Foo| {
super<R|kotlin/Any|>()
}

public final val number: R|kotlin/Int| = R|<local>/number|
public get(): R|kotlin/Int|

public final val letter: R|kotlin/Char| = R|<local>/letter|
public get(): R|kotlin/Char|

public final operator fun component1(): R|kotlin/Int|

public final operator fun component2(): R|kotlin/Char|

public final fun copy(number: R|kotlin/Int| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.number|, letter: R|kotlin/Char| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.letter|): R|com/javiersc/kotlin/kopy/playground/Foo|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdate|() public final infix fun <U> R|U|.update(transform: R|(U) -> U|): R|kotlin/Unit| {
}

public final val _atomic: R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|
public get(): R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionSet|() public final infix fun <S> R|S|.set(other: R|S|): R|kotlin/Unit| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionCopy|() public final infix fun copy(copy: R|com/javiersc/kotlin/kopy/playground/Foo.() -> kotlin/Unit|): R|com/javiersc/kotlin/kopy/playground/Foo| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdateEach|() public final infix fun <UE> R|kotlin/collections/Iterable<UE>|.updateEach(transform: R|(UE) -> UE|): R|kotlin/Unit| {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// OPT_IN: com.javiersc.kotlin.kopy.KopyOptIn
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_CLASS -MISSING_DEPENDENCY_SUPERCLASS

package com.javiersc.kotlin.kopy.playground

import com.javiersc.kotlin.kopy.Kopy

@Kopy
data class Foo(val number: Int, val letter: Char)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FILE: invoke.kt
package com.javiersc.kotlin.kopy.playground

@R|com/javiersc/kotlin/kopy/Kopy|() public final data class Foo : R|kotlin/Any| {
public constructor(number: R|kotlin/Int|, letter: R|kotlin/Char|): R|com/javiersc/kotlin/kopy/playground/Foo| {
super<R|kotlin/Any|>()
}

public final val number: R|kotlin/Int| = R|<local>/number|
public get(): R|kotlin/Int|

public final val letter: R|kotlin/Char| = R|<local>/letter|
public get(): R|kotlin/Char|

public final operator fun component1(): R|kotlin/Int|

public final operator fun component2(): R|kotlin/Char|

public final fun copy(number: R|kotlin/Int| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.number|, letter: R|kotlin/Char| = this@R|com/javiersc/kotlin/kopy/playground/Foo|.R|com/javiersc/kotlin/kopy/playground/Foo.letter|): R|com/javiersc/kotlin/kopy/playground/Foo|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdate|() public final infix fun <U> R|U|.update(transform: R|(U) -> U|): R|kotlin/Unit| {
}

public final val _atomic: R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|
public get(): R|kotlinx/atomicfu/AtomicRef<com/javiersc/kotlin/kopy/playground/Foo>|

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionSet|() public final infix fun <S> R|S|.set(other: R|S|): R|kotlin/Unit| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionUpdateEach|() public final infix fun <UE> R|kotlin/collections/Iterable<UE>|.updateEach(transform: R|(UE) -> UE|): R|kotlin/Unit| {
}

@R|com/javiersc/kotlin/kopy/KopyOptIn|() @R|com/javiersc/kotlin/kopy/KopyFunctionInvoke|() public final operator infix fun invoke(copy: R|com/javiersc/kotlin/kopy/playground/Foo.() -> kotlin/Unit|): R|com/javiersc/kotlin/kopy/playground/Foo| {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// OPT_IN: com.javiersc.kotlin.kopy.KopyOptIn
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_CLASS -MISSING_DEPENDENCY_SUPERCLASS

package com.javiersc.kotlin.kopy.playground

import com.javiersc.kotlin.kopy.Kopy

@Kopy
data class Foo(val number: Int, val letter: Char)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@


package com.javiersc.kotlin.kopy.compiler;

import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.regex.Pattern;

/** This class is generated by {@link com.javiersc.kotlin.kopy.compiler.GenerateKotlinCompilerTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("test-data/diagnostics-kopy-functions/all")
@TestDataPath("$PROJECT_ROOT")
public class KopyAllDiagnosticTestGenerated extends AbstractKopyAllDiagnosticTest {
@Test
@TestMetadata("all.kt")
public void testAll() {
runTest("test-data/diagnostics-kopy-functions/all/all.kt");
}

@Test
public void testAllFilesPresentInAll() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("test-data/diagnostics-kopy-functions/all"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@


package com.javiersc.kotlin.kopy.compiler;

import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.regex.Pattern;

/** This class is generated by {@link com.javiersc.kotlin.kopy.compiler.GenerateKotlinCompilerTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("test-data/diagnostics-kopy-functions/copy")
@TestDataPath("$PROJECT_ROOT")
public class KopyCopyDiagnosticTestGenerated extends AbstractKopyCopyDiagnosticTest {
@Test
public void testAllFilesPresentInCopy() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("test-data/diagnostics-kopy-functions/copy"), Pattern.compile("^(.+)\\.kt$"), null, true);
}

@Test
@TestMetadata("copy.kt")
public void testCopy() {
runTest("test-data/diagnostics-kopy-functions/copy/copy.kt");
}
}
Loading

0 comments on commit 74ecc4b

Please sign in to comment.