Skip to content

Commit a37db4b

Browse files
committed
Add custom caching strategy support
1 parent aaf53ec commit a37db4b

File tree

2 files changed

+51
-53
lines changed

2 files changed

+51
-53
lines changed

src/main/kotlin/com/soberg/kotlin/aoc/api/AdventOfCodeInputApi.kt

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,22 @@ import kotlin.io.path.writeLines
1414

1515
object AdventOfCodeInputApi {
1616

17-
suspend fun readInputFromNetwork(
17+
/** @return A [Result] indicating success or failure*/
18+
suspend fun readInput(
1819
cachingStrategy: CachingStrategy,
1920
year: Int,
2021
day: Int,
2122
sessionToken: String,
22-
): Result<List<String>> {
23+
): Result<List<String>> = runCatching {
2324
// If cache exists, read from it and return immediately.
24-
readFromCache(cachingStrategy, year, day)?.let { cachedLines ->
25+
cachingStrategy.tryRead(year, day)?.let { cachedLines ->
2526
return Result.success(cachedLines)
2627
}
2728

28-
return runCatching {
29-
tryReadInputFromNetwork(year, day, sessionToken)
30-
}.onSuccess { lines ->
31-
// Side effect, store in cache.
32-
writeToCache(cachingStrategy, year, day, lines)
33-
}
34-
}
35-
36-
private fun readFromCache(
37-
cachingStrategy: CachingStrategy,
38-
year: Int,
39-
day: Int,
40-
): List<String>? = when (cachingStrategy) {
41-
CachingStrategy.None -> null
42-
is CachingStrategy.LocalTextFile -> {
43-
val path = Path(cachingStrategy.cacheDirPath, "$year", "$day.txt")
44-
if (path.exists()) {
45-
path.readLines()
46-
} else {
47-
null
48-
}
49-
}
29+
tryReadInputFromNetwork(year, day, sessionToken)
30+
}.onSuccess { lines ->
31+
// Side effect, store in cache.
32+
cachingStrategy.write(year, day, lines)
5033
}
5134

5235
private suspend fun tryReadInputFromNetwork(
@@ -55,7 +38,7 @@ object AdventOfCodeInputApi {
5538
sessionToken: String,
5639
): List<String> {
5740
val response: HttpResponse = AdventOfCodeKtorClient.create().use { client ->
58-
readInputFromNetwork(client, year, day, sessionToken)
41+
readInput(client, year, day, sessionToken)
5942
}
6043
if (response.status.value in 200..299) {
6144
return response.bodyAsText().lines()
@@ -64,7 +47,7 @@ object AdventOfCodeInputApi {
6447
}
6548
}
6649

67-
private suspend fun readInputFromNetwork(
50+
private suspend fun readInput(
6851
client: HttpClient,
6952
year: Int,
7053
day: Int,
@@ -73,35 +56,55 @@ object AdventOfCodeInputApi {
7356
header("Cookie", "session=$sessionToken")
7457
}
7558

76-
private fun writeToCache(
77-
cachingStrategy: CachingStrategy,
78-
year: Int,
79-
day: Int,
80-
lines: List<String>,
81-
) {
82-
when (cachingStrategy) {
83-
is CachingStrategy.LocalTextFile -> {
84-
val path = Path(cachingStrategy.cacheDirPath, "$year", "$day.txt")
59+
sealed interface CachingStrategy {
60+
61+
/** @return Lines of text from this cache if it exists, null otherwise. */
62+
fun tryRead(year: Int, day: Int): List<String>?
63+
64+
/** @return Attempts to write the provided [lines] to an output file, if applicable. */
65+
fun write(year: Int, day: Int, lines: List<String>)
66+
67+
/** Don't try to cache this to a local file, instead just always read from network. */
68+
data object None : CachingStrategy {
69+
override fun tryRead(year: Int, day: Int): List<String>? = null
70+
71+
override fun write(year: Int, day: Int, lines: List<String>) {
72+
/* Do nothing. */
73+
}
74+
}
75+
76+
/** Cache to a local text file from network on first read, then return from the local text file. */
77+
data class LocalTextFile(
78+
val cacheDirPath: String,
79+
) : CachingStrategy {
80+
override fun tryRead(year: Int, day: Int): List<String>? {
81+
val path = Path(cacheDirPath, "$year", "$day.txt")
82+
return if (path.exists()) {
83+
path.readLines()
84+
} else {
85+
null
86+
}
87+
}
88+
89+
override fun write(year: Int, day: Int, lines: List<String>) {
90+
val path = Path(cacheDirPath, "$year", "$day.txt")
8591
if (!path.exists()) {
8692
Files.createDirectories(path.parent)
8793
path.createFile()
8894
}
8995
path.writeLines(lines)
9096
}
91-
92-
CachingStrategy.None -> {
93-
/* Do nothing. */
94-
}
9597
}
96-
}
9798

98-
sealed interface CachingStrategy {
99-
/** Don't try to cache this to a local file, instead just always read from network. */
100-
data object None : CachingStrategy
99+
class Custom(
100+
val tryRead: (year: Int, day: Int) -> List<String>?,
101+
val write: (year: Int, day: Int, lines: List<String>) -> Unit,
102+
) : CachingStrategy {
103+
override fun tryRead(year: Int, day: Int): List<String>? = tryRead(year, day)
101104

102-
/** Cache to a local text file from network on first read, then return from the local text file. */
103-
data class LocalTextFile(
104-
val cacheDirPath: String,
105-
) : CachingStrategy
105+
override fun write(year: Int, day: Int, lines: List<String>) {
106+
write(year, day, lines)
107+
}
108+
}
106109
}
107110
}

src/main/kotlin/com/soberg/kotlin/aoc/api/AdventOfCodeKtorClient.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.soberg.kotlin.aoc.api
22

33
import io.ktor.client.HttpClient
44
import io.ktor.client.HttpClientConfig
5-
import io.ktor.client.engine.HttpClientEngine
65
import io.ktor.client.engine.HttpClientEngineConfig
76
import io.ktor.client.engine.HttpClientEngineFactory
87
import io.ktor.client.engine.okhttp.OkHttp
@@ -19,10 +18,6 @@ internal object AdventOfCodeKtorClient {
1918
engineFactory: HttpClientEngineFactory<HttpClientEngineConfig> = OkHttp,
2019
) = HttpClient(engineFactory) { applyConfig() }
2120

22-
fun create(
23-
engine: HttpClientEngine,
24-
) = HttpClient(engine) { applyConfig() }
25-
2621
private fun HttpClientConfig<*>.applyConfig() {
2722
install(HttpTimeout) {
2823
socketTimeoutMillis = Timeout.inWholeMilliseconds

0 commit comments

Comments
 (0)