Skip to content

Commit 55cd36d

Browse files
committed
feat: add contents api
1 parent c8c3964 commit 55cd36d

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

plugins/common/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dependencies {
2-
compileOnly(libs.kotlinx.serialization.core)
2+
compileOnly(libs.kaml)
33
compileOnly(libs.minecraftserialization.adventure)
44
compileOnly(libs.minecraftserialization.valueproviders)
55
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package net.azisaba.vanilife
2+
3+
import net.kyori.adventure.key.Key
4+
5+
interface Contents<T> {
6+
fun byKey(key: Key): T?
7+
8+
fun byKeyOrThrow(key: Key): T = requireNotNull(byKey(key)) { "Content '$key' not found" }
9+
10+
fun all(): Set<T>
11+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package net.azisaba.vanilife
2+
3+
import com.charleskorn.kaml.PolymorphismStyle
4+
import com.charleskorn.kaml.Yaml
5+
import com.charleskorn.kaml.YamlConfiguration
6+
import kotlinx.serialization.KSerializer
7+
import net.kyori.adventure.key.Key
8+
import org.bukkit.plugin.Plugin
9+
import java.nio.file.Path
10+
import java.util.concurrent.atomic.AtomicReference
11+
import kotlin.io.path.*
12+
13+
abstract class DynamicContents<T>(
14+
val name: String, private val serializer: KSerializer<T>, private val yaml: Yaml = DEFAULT_YAML,
15+
) {
16+
private val mapReference: AtomicReference<Map<Key, T>?> = AtomicReference(null)
17+
18+
fun byKey(key: Key): Holder<T>? = requireLoaded()[key]?.let { Holder(key, it) }
19+
20+
fun all(): Collection<T> = requireLoaded().values
21+
22+
fun bootstrap(plugin: Plugin) {
23+
val contentsRoot = plugin.dataFolder.toPath().resolve(name)
24+
contentsRoot.createDirectories()
25+
26+
require(contentsRoot.isDirectory()) {
27+
"Path is not a directory: $contentsRoot"
28+
}
29+
30+
val newMap = contentsRoot.listDirectoryEntries()
31+
.filter(Path::isDirectory)
32+
.flatMap { namespaceDir ->
33+
namespaceDir.listDirectoryEntries("*.yml")
34+
.sortedBy(Path::name)
35+
.map { file -> file.key() to file.deserialized() }
36+
}
37+
.toMap()
38+
39+
mapReference.set(newMap)
40+
}
41+
42+
private fun requireLoaded(): Map<Key, T> =
43+
mapReference.get() ?: throw IllegalStateException("You are trying to access contents '$name' too early")
44+
45+
private fun Path.key(): Key {
46+
val namespace = parent?.name ?: throw IllegalArgumentException("Cannot derive namespace from path: $this")
47+
val value = nameWithoutExtension
48+
return try {
49+
Key.key(namespace, value)
50+
} catch (e: Exception) {
51+
throw IllegalArgumentException("Invalid content key: $namespace:$value ($this)", e)
52+
}
53+
}
54+
55+
private fun Path.deserialized(): T {
56+
val text = try {
57+
readText()
58+
} catch (e: Exception) {
59+
throw IllegalStateException("Failed to read file: $this", e)
60+
}
61+
62+
return try {
63+
yaml.decodeFromString(serializer, text)
64+
} catch (e: Exception) {
65+
throw IllegalArgumentException("Failed to parse YAML file: $this\n$text", e)
66+
}
67+
}
68+
69+
private companion object {
70+
val DEFAULT_YAML: Yaml = Yaml(
71+
configuration = YamlConfiguration(
72+
polymorphismStyle = PolymorphismStyle.Property,
73+
polymorphismPropertyName = "kind",
74+
)
75+
)
76+
}
77+
78+
data class Holder<T>(val key: Key, val value: T)
79+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package net.azisaba.vanilife
2+
3+
import net.kyori.adventure.key.Key
4+
import net.kyori.adventure.key.Keyed
5+
import kotlin.reflect.KClass
6+
7+
abstract class EnumContents<T>(
8+
private val kClass: KClass<T>,
9+
) : Contents<T> where T : Enum<T>, T : Keyed {
10+
private val byKey: Map<Key, T> = kClass.java.enumConstants.associateBy { it.key() }
11+
12+
override fun byKey(key: Key): T? = byKey[key]
13+
14+
override fun all(): Set<T> = byKey.values.toSet()
15+
}

0 commit comments

Comments
 (0)