Inject challenges input
This commit is contained in:
parent
b470715f12
commit
7e31325d16
@ -19,9 +19,24 @@ object Libs {
|
||||
const val processor = "org.openjdk.jmh:jmh-generator-annprocess:1.26"
|
||||
}
|
||||
|
||||
object Micronaut {
|
||||
private const val version = "2.2.0"
|
||||
const val inject = "io.micronaut:micronaut-inject:$version"
|
||||
const val core = "io.micronaut:micronaut-core:$version"
|
||||
const val kotlin = "io.micronaut.kotlin:micronaut-kotlin-extension-functions:$version"
|
||||
const val processor = "io.micronaut:micronaut-inject-java:$version"
|
||||
}
|
||||
|
||||
object Slf4J {
|
||||
const val api = "org.slf4j:slf4j-api:1.7.25"
|
||||
const val logback = "ch.qos.logback:logback-classic:1.2.3"
|
||||
const val kotlin = "io.github.microutils:kotlin-logging-jvm:2.0.3"
|
||||
}
|
||||
|
||||
object Tests {
|
||||
const val assertJ = "org.assertj:assertj-core:3.18.1"
|
||||
const val junit = "org.junit.jupiter:junit-jupiter:5.7.0"
|
||||
const val jimfs = "com.google.jimfs:jimfs:1.1"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
plugins {
|
||||
id("kotlin-convention")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":utils"))
|
||||
|
||||
implementation(Libs.Micronaut.core)
|
||||
implementation(Libs.Micronaut.inject)
|
||||
implementation(Libs.Micronaut.kotlin)
|
||||
kapt(Libs.Micronaut.processor)
|
||||
}
|
||||
|
||||
@ -1,22 +1,30 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.Resources
|
||||
import be.vandewalleh.aoc.utils.input.Day
|
||||
import be.vandewalleh.aoc.utils.input.Input
|
||||
import be.vandewalleh.aoc.utils.input.Lines
|
||||
import io.micronaut.context.BeanContext
|
||||
import io.micronaut.context.getBean
|
||||
|
||||
object Day01 {
|
||||
@Day(1)
|
||||
class Day01(@Lines input: Input<IntArray>) {
|
||||
private val items = input.value
|
||||
|
||||
fun part1(list: List<Int>): Int? {
|
||||
list.forEach { a ->
|
||||
list.forEach { b ->
|
||||
fun part1(): Int? {
|
||||
items.forEach { a ->
|
||||
items.forEach { b ->
|
||||
if (a + b == 2020) return a * b
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun part2(list: List<Int>): Int? {
|
||||
list.forEach { a ->
|
||||
list.forEach { b ->
|
||||
list.forEach { c ->
|
||||
fun part2(): Int? {
|
||||
items.forEach { a ->
|
||||
items.forEach { b ->
|
||||
items.forEach { c ->
|
||||
if (a + b + c == 2020) return a * b * c
|
||||
}
|
||||
}
|
||||
@ -27,7 +35,7 @@ object Day01 {
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val input = Resources.ints("day01.txt").toList()
|
||||
println(Day01.part1(input))
|
||||
println(Day01.part2(input))
|
||||
val day = BeanContext.run().getBean<Day01>()
|
||||
println(day.part1())
|
||||
println(day.part2())
|
||||
}
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
package be.vandewalleh.aoc.days
|
||||
|
||||
import be.vandewalleh.aoc.utils.Resources
|
||||
import io.micronaut.context.BeanContext
|
||||
import io.micronaut.context.getBean
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class Day01Test {
|
||||
|
||||
private val input = Resources.ints("day01.txt").toList()
|
||||
private val day = BeanContext.run().getBean<Day01>()
|
||||
|
||||
@Test
|
||||
fun `part1 result`() {
|
||||
assertThat(Day01.part1(input)).isEqualTo(32064)
|
||||
assertThat(day.part1()).isEqualTo(32064)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `part2 result`() {
|
||||
assertThat(Day01.part2(input)).isEqualTo(193598720)
|
||||
assertThat(day.part2()).isEqualTo(193598720)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,3 +1,18 @@
|
||||
plugins {
|
||||
id("kotlin-convention")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(Libs.Micronaut.core)
|
||||
implementation(Libs.Micronaut.inject)
|
||||
implementation(Libs.Micronaut.kotlin)
|
||||
kapt(Libs.Micronaut.processor)
|
||||
|
||||
implementation(Libs.Slf4J.api)
|
||||
implementation(Libs.Slf4J.kotlin)
|
||||
runtimeOnly(Libs.Slf4J.logback)
|
||||
|
||||
testImplementation(Libs.Tests.jimfs)
|
||||
kaptTest(Libs.Micronaut.processor)
|
||||
}
|
||||
|
||||
1
utils/src/main/kotlin/be/vandewalleh/aoc/utils/Index.kt
Normal file
1
utils/src/main/kotlin/be/vandewalleh/aoc/utils/Index.kt
Normal file
@ -0,0 +1 @@
|
||||
package be.vandewalleh.aoc.utils
|
||||
@ -1,28 +0,0 @@
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package be.vandewalleh.aoc.utils
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
object Resources {
|
||||
private fun path(name: String): Path {
|
||||
val url = if (name.startsWith('/')) javaClass.getResource(name)
|
||||
else javaClass.getResource("/$name")
|
||||
return Path.of(url.toURI())
|
||||
}
|
||||
|
||||
private fun Path.lines() = Files.lines(this)
|
||||
.asSequence()
|
||||
.filter { it.isNotBlank() }
|
||||
.map { it.trim() }
|
||||
|
||||
private fun Path.text() = Files.readString(this).trim()
|
||||
|
||||
fun text(name: String) = path(name).text()
|
||||
fun lines(name: String) = path(name).lines()
|
||||
fun ints(name: String) = lines(name).map { it.toInt() }
|
||||
fun csv(name: String) = path(name).text().splitToSequence(",")
|
||||
fun csvLong(name: String) = csv(name).map { it.toLong() }
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
import io.micronaut.context.annotation.Prototype
|
||||
import io.micronaut.core.annotation.Introspected
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Prototype
|
||||
@Qualifier
|
||||
@Introspected
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Day(val day: Int)
|
||||
|
||||
@Qualifier
|
||||
@Prototype
|
||||
annotation class DayInput
|
||||
|
||||
@DayInput
|
||||
annotation class Csv
|
||||
|
||||
@DayInput
|
||||
annotation class Text
|
||||
|
||||
@DayInput
|
||||
annotation class Lines
|
||||
@ -0,0 +1,7 @@
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
/**
|
||||
* Wrapper class so that micronaut is not confused with an other injectable
|
||||
* container type when using an iterable / array
|
||||
*/
|
||||
data class Input<T>(val value: T)
|
||||
@ -0,0 +1,76 @@
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
import io.micronaut.context.annotation.Factory
|
||||
import io.micronaut.inject.InjectionPoint
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.readLines
|
||||
import kotlin.io.path.readText
|
||||
|
||||
/**
|
||||
* Load challenge inputs and convert them to the appropriate format
|
||||
*
|
||||
* contains horrible hacks while waiting for generic support in bean injection
|
||||
* @see [micronaut-2775](https://github.com/micronaut-projects/micronaut-core/issues/2775)
|
||||
*/
|
||||
@Factory
|
||||
@ExperimentalPathApi
|
||||
class InputFactory(private val resourceLoader: ResourceLoader) {
|
||||
|
||||
@Csv
|
||||
fun csvIntArray(injectionPoint: InjectionPoint<*>): Input<IntArray> = injectionPoint
|
||||
.read()
|
||||
.split(",")
|
||||
.map { it.toInt() }
|
||||
.toIntArray()
|
||||
.wrap()
|
||||
|
||||
@Lines
|
||||
fun linesIntArray(injectionPoint: InjectionPoint<*>): Input<*> =
|
||||
when (val param = injectionPoint.typeNameOfAnnotation<Lines>()) {
|
||||
"int[]" -> injectionPoint
|
||||
.lines()
|
||||
.map { it.toInt() }
|
||||
.toList()
|
||||
.toIntArray()
|
||||
.wrap()
|
||||
"java.util.List<java.lang.String>" -> injectionPoint
|
||||
.lines()
|
||||
.toList()
|
||||
.wrap()
|
||||
else -> error("Unsupported type $param")
|
||||
}
|
||||
|
||||
@Text
|
||||
fun string(injectionPoint: InjectionPoint<*>): Input<String> =
|
||||
injectionPoint.read().wrap()
|
||||
|
||||
|
||||
private fun <T> T.wrap() = Input(this)
|
||||
|
||||
private inline fun <reified T : Annotation> InjectionPoint<*>.typeNameOfAnnotation() = declaringBean
|
||||
.constructor
|
||||
.arguments
|
||||
.find { it.hasAnnotation<T>() }
|
||||
?.typeName
|
||||
?.removePrefix("be.vandewalleh.aoc.utils.input.Input<")
|
||||
?.removeSuffix(">")
|
||||
?: error("??")
|
||||
|
||||
private fun InjectionPoint<*>.path() = resourceLoader.ofDay(getDay(this))
|
||||
private fun InjectionPoint<*>.read() = path().readText().trim()
|
||||
private fun InjectionPoint<*>.lines() = path().readLines()
|
||||
.asSequence()
|
||||
.filter { it.isNotBlank() }
|
||||
|
||||
private fun getDay(injectionPoint: InjectionPoint<*>): Int {
|
||||
val dayAnnotation = injectionPoint
|
||||
.declaringBean
|
||||
.findAnnotation<Day>()
|
||||
|
||||
if (dayAnnotation.isEmpty)
|
||||
error("@DayInput cannot only be used on classes annotated with ${Day::class.qualifiedName}")
|
||||
|
||||
return dayAnnotation.get().intValue("day").asInt
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
import io.micronaut.core.annotation.AnnotationMetadataDelegate
|
||||
import io.micronaut.core.annotation.AnnotationMetadataProvider
|
||||
import io.micronaut.core.annotation.AnnotationValue
|
||||
import io.micronaut.core.beans.BeanIntrospection
|
||||
import java.util.*
|
||||
|
||||
internal inline fun <reified T> getIntrospection(): BeanIntrospection<T> =
|
||||
BeanIntrospection.getIntrospection(T::class.java)
|
||||
|
||||
internal inline fun <reified T : Annotation> AnnotationMetadataDelegate.getAnnotation(): AnnotationValue<T>? =
|
||||
getAnnotation(T::class.java)
|
||||
|
||||
internal inline fun <reified T : Annotation> AnnotationMetadataProvider.findAnnotation(): Optional<AnnotationValue<T>> =
|
||||
findAnnotation(T::class.java)
|
||||
|
||||
internal inline fun <reified T : Annotation> AnnotationMetadataProvider.hasAnnotation(): Boolean =
|
||||
findAnnotation(T::class.java).isPresent
|
||||
@ -0,0 +1,21 @@
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface ResourceLoader {
|
||||
fun ofDay(day: Int): Path
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class ResourceLoaderImpl : ResourceLoader {
|
||||
override fun ofDay(day: Int): Path {
|
||||
val resourcePath = buildString {
|
||||
append("/day")
|
||||
append(day.toString().padStart(2, '0'))
|
||||
append(".txt")
|
||||
}
|
||||
val url = javaClass.getResource(resourcePath)
|
||||
return Path.of(url.toURI())
|
||||
}
|
||||
}
|
||||
14
utils/src/main/resources/logback.xml
Normal file
14
utils/src/main/resources/logback.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<withJansi>true</withJansi>
|
||||
<encoder>
|
||||
<pattern>%cyan(%d{YYYY-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{36}) - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="io.micronaut" level="INFO"/>
|
||||
<logger name="io.micronaut.context.lifecycle" level="INFO"/>
|
||||
</configuration>
|
||||
1
utils/src/test/kotlin/Index.kt
Normal file
1
utils/src/test/kotlin/Index.kt
Normal file
@ -0,0 +1 @@
|
||||
package be.vandewalleh.aoc.utils
|
||||
84
utils/src/test/kotlin/input/DayTest.kt
Normal file
84
utils/src/test/kotlin/input/DayTest.kt
Normal file
@ -0,0 +1,84 @@
|
||||
package be.vandewalleh.aoc.utils.input
|
||||
|
||||
import com.google.common.jimfs.Jimfs
|
||||
import io.micronaut.context.BeanContext
|
||||
import io.micronaut.context.annotation.Replaces
|
||||
import io.micronaut.context.getBean
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import javax.inject.Singleton
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.writeText
|
||||
|
||||
class DayTest {
|
||||
|
||||
@Singleton
|
||||
@Replaces(ResourceLoaderImpl::class)
|
||||
@ExperimentalPathApi
|
||||
class FakeResourceLoader : ResourceLoader {
|
||||
private val inMemoryFs = Jimfs.newFileSystem().apply {
|
||||
getPath("1").writeText("blablabla")
|
||||
getPath("2").writeText("1,+2,3,4,-5")
|
||||
getPath("3")
|
||||
.writeText(
|
||||
"""
|
||||
1779
|
||||
1737
|
||||
1537
|
||||
1167
|
||||
1804
|
||||
1873
|
||||
""".trimIndent()
|
||||
)
|
||||
getPath("4")
|
||||
.also { println(it) }
|
||||
.writeText(
|
||||
"""
|
||||
a
|
||||
bb
|
||||
ccc
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
override fun ofDay(day: Int) = inMemoryFs.getPath(day.toString())
|
||||
}
|
||||
|
||||
@Day(1)
|
||||
class TextStringExample(@Text val input: Input<String>)
|
||||
|
||||
@Day(2)
|
||||
class CsvIntArrayExample(@Csv val input: Input<IntArray>)
|
||||
|
||||
@Day(3)
|
||||
class IntLinesExample(@Lines val input: Input<IntArray>)
|
||||
|
||||
@Day(4)
|
||||
class StringLinesExample(@Lines val input: Input<List<String>>)
|
||||
|
||||
private inline fun <reified T> createDay() = BeanContext.run().getBean<T>()
|
||||
|
||||
@Test
|
||||
fun `check @Text String`() {
|
||||
val day = createDay<TextStringExample>()
|
||||
assertThat(day.input.value).isEqualTo("blablabla")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check @Csv IntArray`() {
|
||||
val day = createDay<CsvIntArrayExample>()
|
||||
assertThat(day.input.value).containsExactly(1, +2, 3, 4, -5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check @Lines IntArray`() {
|
||||
val day = createDay<IntLinesExample>()
|
||||
assertThat(day.input.value).containsExactly(1779, 1737, 1537, 1167, 1804, 1873)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check @Lines strings`() {
|
||||
val day = createDay<StringLinesExample>()
|
||||
assertThat(day.input.value).containsExactly("a", "bb", "ccc")
|
||||
}
|
||||
|
||||
}
|
||||
4
utils/src/test/resources/junit-platform.properties
Normal file
4
utils/src/test/resources/junit-platform.properties
Normal file
@ -0,0 +1,4 @@
|
||||
junit.jupiter.testinstance.lifecycle.default=per_class
|
||||
junit.jupiter.execution.parallel.enabled=true
|
||||
junit.jupiter.execution.parallel.mode.default=same_thread
|
||||
junit.jupiter.execution.parallel.mode.classes.default=concurrent
|
||||
Loading…
x
Reference in New Issue
Block a user