Skip to content

Commit

Permalink
Annotation processor for [at]bounded
Browse files Browse the repository at this point in the history
  • Loading branch information
andrzejressel authored and nomisRev committed Apr 7, 2018
1 parent b9d9871 commit 3541590
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package arrow.optics

import arrow.common.utils.fullName
import me.eugeniomarletti.kotlin.metadata.escapedClassName
import java.io.File

class BoundLensGenerator(
private val annotatedList: Collection<AnnotatedOptic>,
private val generatedDir: File
) {

private val boundLens = "arrow.optics.BoundSetter"

fun generate() = annotatedList.map(this::processElement)
.map { (element, funs) ->
"${boundAnnotationClass.simpleName}.${element.classData.`package`}.${element.type.simpleName.toString().toLowerCase()}.kt" to
funs.joinToString(prefix = "package ${element.classData.`package`.escapedClassName}\n\n", separator = "\n")
}.forEach { (name, fileString) -> File(generatedDir, name).writeText(fileString) }

private fun String.toUpperCamelCase(): String = split(" ").joinToString("", transform = String::capitalize)

private fun processElement(annotatedOptic: AnnotatedOptic): Pair<AnnotatedOptic, List<String>> =
annotatedOptic to annotatedOptic.targets.map { variable ->
val sourceClassName = annotatedOptic.classData.fullName.escapedClassName
val sourceName = annotatedOptic.type.simpleName.toString().decapitalize()
val targetClassName = variable.fullName
val targetName = variable.paramName

"""
|val <T> $boundLens<T, $sourceClassName>.$targetName: $boundLens<T, $targetClassName> get() = this.compose($sourceName${targetName.toUpperCamelCase()}())
""".trimMargin()
}

}
11 changes: 9 additions & 2 deletions arrow-optics/src/main/kotlin/arrow/optics/bound.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package arrow.optics

class BoundSetter<S, A>(private val value: S, private val setter: Setter<S, A>) {
import arrow.core.Option

class BoundSetter<S, A>(val value: S, val setter: Setter<S, A>) {

fun <T> compose(other: Setter<A, T>) = BoundSetter(value, setter + other)
fun <T> compose(other: Optional<A, T>) = BoundSetter(value, setter + other)
Expand All @@ -14,4 +16,9 @@ class BoundSetter<S, A>(private val value: S, private val setter: Setter<S, A>)
fun set(a: A) = setter.set(value, a)
}

fun <T> T.setter() = BoundSetter(this, Setter.id())
fun <T> T.setter() = BoundSetter(this, Setter.id())

val <T, A> BoundSetter<T, A?>.nullable get(): BoundSetter<T, A> = compose(nullableOptional())

val <T, A> BoundSetter<T, Option<A>>.some get(): BoundSetter<T, A> = compose(optionOptional())

30 changes: 30 additions & 0 deletions arrow-optics/src/test/kotlin/arrow/optics/BoundedTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package arrow.optics

import arrow.bounded
import arrow.lenses
import io.kotlintest.matchers.shouldBe
import io.kotlintest.specs.StringSpec

@bounded data class Street(val number: Int, val name: String)
@bounded data class Address(val city: String, val street: Street)
@bounded data class Company(val name: String, val address: Address)
@bounded data class Employee(val name: String, val company: Company?)

//TODO: Rewrite to annotation processor test when multiple file support is implemented
class BoundedTest: StringSpec() {

init {

"@bounded classes are generated properly" {

val employee = Employee("John Doe", Company("Kategory", Address("Functional city", Street(42, "lambda street"))))
val newEmployee = employee.setter().company.nullable.address.street.name.modify { it.capitalize() }

val expected = Employee("John Doe", Company("Kategory", Address("Functional city", Street(42, "Lambda street"))))

newEmployee shouldBe expected
}

}

}
3 changes: 0 additions & 3 deletions modules/core/arrow-annotations-processor/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import org.gradle.internal.jvm.Jvm
import org.gradle.api.GradleException

apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package arrow.optics

import arrow.bounded
import arrow.isos
import arrow.lenses
import arrow.optionals
Expand All @@ -23,4 +24,9 @@ val isosAnnotationTarget = "data class"
val optionalsAnnotationKClass = optionals::class
val optionalsAnnotationClass = optionalsAnnotationKClass.java
val optionalsAnnotationName = "@" + optionalsAnnotationClass.simpleName
val optionalsAnnotationTarget = "data class"
val optionalsAnnotationTarget = "data class"

val boundAnnotationKClass = bounded::class
val boundAnnotationClass = boundAnnotationKClass.java
val boundAnnotationName = "@" + boundAnnotationClass.simpleName
val boundAnnotationTarget = "data class"
4 changes: 4 additions & 0 deletions modules/core/arrow-annotations/src/main/java/arrow/optics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ annotation class isos
@Retention(SOURCE)
@Target(CLASS)
annotation class optionals

@Retention(SOURCE)
@Target(CLASS)
annotation class bounded
1 change: 1 addition & 0 deletions modules/optics/arrow-optics/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {

compile project(':arrow-annotations')
kapt project(':arrow-annotations-processor')
kaptTest project(':arrow-annotations-processor')
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
Expand Down

0 comments on commit 3541590

Please sign in to comment.