Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
52e2a52468
|
|||
|
2f4011c144
|
|||
|
a413fab8d5
|
|||
|
615d3810a0
|
|||
|
e6ef13ff7f
|
|||
|
784309b426
|
|||
|
affc1de0f5
|
|||
|
d69eb85d81
|
|||
|
1b9958f980
|
|||
|
e879050a33
|
|||
|
7673399f3e
|
|||
|
ad9c22243a
|
|||
|
56ed60e532
|
|||
|
0e5a907446
|
|||
|
2e9f4c1689
|
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
.gitattributes text eol=lf
|
||||
*.sh text eol=lf
|
||||
gradlew text eol=lf
|
||||
gradlew.bat text eol=crlf
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -5,6 +5,11 @@ build/
|
||||
!**/src/test/**/build/
|
||||
.kotlin/
|
||||
|
||||
### Gradle in Subprojects ###
|
||||
*/gradlew
|
||||
*/gradlew.bat
|
||||
*/gradle/wrapper
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
*.iws
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -5,15 +5,20 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.7.2] - 2026-03-29
|
||||
### Changed
|
||||
- `minecraft`: Added support for Minecraft 26.1.
|
||||
This includes some required changes in the build process. So this release is published for all versions of Minecraft.
|
||||
## [Unreleased]
|
||||
|
||||
## [0.7.1] - 2026-02-08
|
||||
### Added
|
||||
- `networking`: Added module for Minecraft networking.
|
||||
- `coat-bridge`: Added experimental text mapper based on Tweed Serde.
|
||||
|
||||
### Changed
|
||||
- Relicensed to MPL-2.0.
|
||||
- **Breaking**@`core`: Refactored type hierarchy and methods of `StructuredConfigEntry`.
|
||||
- **Breaking**@`serde-*`: Repackaged all classes to bear `tweed5.serde` instead of `tweed5.data` in their packages.
|
||||
- `weaver-pojo-serde-extension`: Slightly changed the `SerdePojoReaderWriterSpec`
|
||||
to be more closely aligned with Java's identifier rules.
|
||||
- `attributes-extension`: The `AttributesReadWriteFilterExtension` now correctly skips non-matching compound entries
|
||||
instead of returning `null` for them.
|
||||
- `serde-hjson`: `inlineCommentType` on `HjsonWriter.Options` now correctly works builder-style.
|
||||
|
||||
## [0.7.0] - 2025-12-19
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class MinecraftModComponentPlugin : Plugin<Project> {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objectFactory.named(Category.LIBRARY))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objectFactory.named(LibraryElements.JAR))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objectFactory.named(Bundling.EXTERNAL))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.JAVA_API))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.JAVA_RUNTIME))
|
||||
|
||||
project.afterEvaluate {
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion.get().toInt())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
jacoco
|
||||
id("io.freefair.lombok")
|
||||
id("de.siphalor.tweed5.unit-tests")
|
||||
id("de.siphalor.tweed5.publishing")
|
||||
id("de.siphalor.tweed5.local-runtime-only")
|
||||
id("de.siphalor.tweed5.expanded-sources-jar")
|
||||
@@ -13,12 +13,6 @@ java {
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
val testAgent = configurations.dependencyScope("mockitoAgent")
|
||||
val testAgentClasspath = configurations.resolvable("testAgentClasspath") {
|
||||
isTransitive = false
|
||||
extendsFrom(testAgent.get())
|
||||
}
|
||||
|
||||
lombok {
|
||||
version = libs.versions.lombok.get()
|
||||
}
|
||||
@@ -39,40 +33,9 @@ dependencies {
|
||||
testImplementation(libs.acl)
|
||||
testImplementation(libs.slf4j.rt)
|
||||
|
||||
testImplementation(platform(libs.junit.platform))
|
||||
testImplementation(libs.junit.core)
|
||||
testRuntimeOnly(libs.junit.launcher)
|
||||
testImplementation(libs.mockito)
|
||||
testAgent(libs.mockito)
|
||||
testImplementation(libs.assertj)
|
||||
testImplementation(project(":generic-test-utils"))
|
||||
}
|
||||
|
||||
tasks.compileTestJava {
|
||||
sourceCompatibility = libs.versions.java.test.get()
|
||||
targetCompatibility = libs.versions.java.test.get()
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
val testAgentFiles = testAgentClasspath.map { it.files }
|
||||
doFirst {
|
||||
jvmArgs(testAgentFiles.get().map { file -> "-javaagent:${file.absolutePath}" })
|
||||
}
|
||||
dependsOn(testAgentClasspath)
|
||||
finalizedBy(tasks.jacocoTestReport)
|
||||
|
||||
useJUnitPlatform()
|
||||
systemProperties(
|
||||
"junit.jupiter.execution.timeout.mode" to "disabled_on_debug",
|
||||
"junit.jupiter.execution.timeout.testable.method.default" to "10s",
|
||||
"junit.jupiter.execution.timeout.thread.mode.default" to "SEPARATE_THREAD",
|
||||
)
|
||||
}
|
||||
|
||||
tasks.jacocoTestReport {
|
||||
dependsOn(tasks.test)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("lib") {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import java.util.Properties
|
||||
|
||||
val rootProperties = Properties()
|
||||
project.layout.settingsDirectory.file("../gradle.properties").asFile.inputStream().use { rootProperties.load(it) }
|
||||
rootProperties.forEach { (key, value) -> project.ext.set(key.toString(), value.toString()) }
|
||||
val rootPropertiesFile = project.layout.settingsDirectory.file("../gradle.properties").asFile
|
||||
if (rootPropertiesFile.exists()) {
|
||||
Properties()
|
||||
.apply { rootPropertiesFile.inputStream().use { load(it) } }
|
||||
.forEach { (key, value) -> project.ext.set(key.toString(), value.toString()) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
plugins {
|
||||
java
|
||||
jacoco
|
||||
}
|
||||
|
||||
val testAgent = configurations.dependencyScope("mockitoAgent")
|
||||
val testAgentClasspath = configurations.resolvable("testAgentClasspath") {
|
||||
isTransitive = false
|
||||
extendsFrom(testAgent.get())
|
||||
}
|
||||
|
||||
dependencies {
|
||||
versionCatalogs.find("libs").ifPresent { libs ->
|
||||
testImplementation(platform(libs.findLibrary("junit.platform").get()))
|
||||
testImplementation(libs.findLibrary("junit.core").get())
|
||||
testRuntimeOnly(libs.findLibrary("junit.launcher").get())
|
||||
testImplementation(libs.findLibrary("mockito").get())
|
||||
testAgent(libs.findLibrary("mockito").get())
|
||||
testImplementation(libs.findLibrary("assertj").get())
|
||||
}
|
||||
}
|
||||
|
||||
tasks.compileTestJava {
|
||||
versionCatalogs.find("libs").ifPresent { libs ->
|
||||
sourceCompatibility = libs.findVersion("java.test").get().requiredVersion
|
||||
targetCompatibility = libs.findVersion("java.test").get().requiredVersion
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
val testAgentFiles = testAgentClasspath.map { it.files }
|
||||
doFirst {
|
||||
jvmArgs(testAgentFiles.get().map { file -> "-javaagent:${file.absolutePath}" })
|
||||
}
|
||||
dependsOn(testAgentClasspath)
|
||||
finalizedBy(tasks.jacocoTestReport)
|
||||
|
||||
useJUnitPlatform()
|
||||
systemProperties(
|
||||
"junit.jupiter.execution.timeout.mode" to "disabled_on_debug",
|
||||
"junit.jupiter.execution.timeout.testable.method.default" to "10s",
|
||||
"junit.jupiter.execution.timeout.thread.mode.default" to "SEPARATE_THREAD",
|
||||
)
|
||||
}
|
||||
|
||||
tasks.jacocoTestReport {
|
||||
dependsOn(tasks.test)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
org.gradle.jvmargs = -Xmx2G
|
||||
org.gradle.configuration-cache = true
|
||||
|
||||
tweed5.version = 0.7.2
|
||||
tweed5.version = 0.7.1
|
||||
|
||||
git.url = https://gitea.siphalor.de/siphalor/tweed5
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradlew
vendored
5
gradlew
vendored
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -114,6 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -171,6 +172,7 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -210,6 +212,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
|
||||
3
gradlew.bat
vendored
3
gradlew.bat
vendored
@@ -70,10 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -7,9 +7,8 @@ plugins {
|
||||
dependencies {
|
||||
implementation("de.siphalor.tweed5:tweed5-conventions")
|
||||
implementation("de.siphalor.tweed5:tweed5-conventions-helpers")
|
||||
implementation(pluginMarker(mcCommonLibs.plugins.smcmtk))
|
||||
implementation(pluginMarker(mcCommonLibs.plugins.fabric.loom))
|
||||
implementation(pluginMarker(mcCommonLibs.plugins.jcyo))
|
||||
implementation(pluginMarker(mcLibs.plugins.fabric.loom))
|
||||
implementation(pluginMarker(libs.plugins.lombok))
|
||||
implementation(pluginMarker(libs.plugins.shadow))
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import de.siphalor.jcyo.gradle.JcyoTask
|
||||
import de.siphalor.tweed5.gradle.plugin.minecraft.mod.MinecraftModded
|
||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||
import net.fabricmc.loom.task.RemapJarTask
|
||||
import net.fabricmc.loom.util.Constants
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("de.siphalor.minecraft-modding-toolkit.project-plugin")
|
||||
id("fabric-loom")
|
||||
id("de.siphalor.tweed5.unit-tests")
|
||||
id("de.siphalor.tweed5.publishing")
|
||||
id("de.siphalor.tweed5.expanded-sources-jar")
|
||||
id("de.siphalor.jcyo")
|
||||
@@ -19,21 +17,14 @@ plugins {
|
||||
id("de.siphalor.tweed5.minecraft.mod.base")
|
||||
}
|
||||
|
||||
val mcCatalog = versionCatalogs.named("mcLibs")
|
||||
val loomPluginId = mcCatalog.findPlugin("fabric.loom").get().get().pluginId
|
||||
if (!loomPluginId.endsWith("-remap")) {
|
||||
project.extensions.extraProperties.set(Constants.Properties.DISABLE_OBFUSCATION, "true")
|
||||
project.extensions.extraProperties.set(Constants.Properties.DONT_REMAP, "true")
|
||||
}
|
||||
|
||||
apply(plugin = loomPluginId)
|
||||
|
||||
val minecraftVersionDescriptor = project.property("minecraft.version.descriptor") as String
|
||||
val mcProps = Properties().apply {
|
||||
val propFile = project.layout.settingsDirectory.file("gradle/mc-$minecraftVersionDescriptor/gradle.properties").asFile
|
||||
propFile.inputStream().use { load(it) }
|
||||
}
|
||||
|
||||
val mcCatalog = versionCatalogs.named("mcLibs")
|
||||
|
||||
group = "de.siphalor.tweed5.minecraft"
|
||||
val archivesBaseName = "${project.name}-mc$minecraftVersionDescriptor"
|
||||
base {
|
||||
@@ -48,12 +39,7 @@ val testmod by sourceSets.creating {
|
||||
runtimeClasspath += sourceSets.main.get().runtimeClasspath
|
||||
}
|
||||
|
||||
smcmtk {
|
||||
useMojangMappings()
|
||||
createModConfigurations(listOf(sourceSets.main.get(), testmod))
|
||||
}
|
||||
|
||||
extensions.configure<LoomGradleExtensionAPI>() {
|
||||
loom {
|
||||
runs {
|
||||
create("testmodClient") {
|
||||
client()
|
||||
@@ -61,6 +47,7 @@ extensions.configure<LoomGradleExtensionAPI>() {
|
||||
source(testmod)
|
||||
}
|
||||
}
|
||||
createRemapConfigurations(testmod)
|
||||
}
|
||||
|
||||
// For some reason dependencyResolutionManagement from the settings.gradle doesn't seem to be passed through correctly,
|
||||
@@ -92,8 +79,12 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"minecraft"(mcCatalog.findLibrary("minecraft").get())
|
||||
"modImplementation"(mcCommonLibs.fabric.loader)
|
||||
minecraft(mcCatalog.findLibrary("minecraft").get())
|
||||
mappings(loom.layered {
|
||||
officialMojangMappings()
|
||||
parchment("org.parchmentmc.data:parchment-$minecraftVersion:${getMcCatalogVersion("parchment")}@zip")
|
||||
})
|
||||
modImplementation(mcCommonLibs.fabric.loader)
|
||||
"modTestmodImplementation"(mcCommonLibs.fabric.loader)
|
||||
|
||||
compileOnly(libs.jspecify.annotations)
|
||||
@@ -101,11 +92,6 @@ dependencies {
|
||||
"testmodImplementation"(sourceSets.main.map { it.output })
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.toVersion(mcCatalog.findVersion("java").get())
|
||||
targetCompatibility = JavaVersion.toVersion(mcCatalog.findVersion("java").get())
|
||||
}
|
||||
|
||||
val jcyoVars = mcProps.stringPropertyNames()
|
||||
.filter { it.startsWith("preprocessor.") }
|
||||
.map { it to mcProps[it] }
|
||||
@@ -131,6 +117,31 @@ lombok {
|
||||
version = libs.versions.lombok.get()
|
||||
}
|
||||
|
||||
val testmodLombokConfigSource = project.layout.settingsDirectory.file("lombok.testmod.config").asFile
|
||||
val testmodLombokConfigTarget = file("src/testmod/lombok.config")
|
||||
val copyTestmodLombokConfig by tasks.register("copyTestmodLombokConfig") {
|
||||
val source = testmodLombokConfigSource
|
||||
val target = testmodLombokConfigTarget
|
||||
inputs.file(source)
|
||||
outputs.file(target)
|
||||
|
||||
doFirst {
|
||||
target.parentFile.mkdirs()
|
||||
Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("compileTestmodJava") {
|
||||
inputs.file(testmodLombokConfigSource)
|
||||
dependsOn(copyTestmodLombokConfig)
|
||||
}
|
||||
afterEvaluate {
|
||||
tasks.named("generateTestmodEffectiveLombokConfig") {
|
||||
inputs.file(testmodLombokConfigSource)
|
||||
dependsOn(copyTestmodLombokConfig)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<Copy>("processResources") {
|
||||
val processMinecraftModResources = tasks.named<Sync>("processMinecraftModResources")
|
||||
dependsOn(processMinecraftModResources)
|
||||
@@ -149,8 +160,7 @@ shadow {
|
||||
addShadowVariantIntoJavaComponent = false
|
||||
}
|
||||
|
||||
tasks.findByName("remapJar")?.apply {
|
||||
this as RemapJarTask
|
||||
tasks.remapJar {
|
||||
dependsOn(tasks.shadowJar)
|
||||
inputFile = tasks.shadowJar.get().archiveFile
|
||||
}
|
||||
|
||||
@@ -8,17 +8,16 @@ dependencies {
|
||||
compileOnly("de.siphalor.tweed5:tweed5-attributes-extension")
|
||||
compileOnly("de.siphalor.tweed5:tweed5-default-extensions")
|
||||
compileOnly("de.siphalor.tweed5:tweed5-weaver-pojo")
|
||||
compileOnly(project(":tweed5-logging", configuration = "minecraftModApiElements"))
|
||||
modCompileOnly(mcLibs.coat)
|
||||
|
||||
listOf(smcmtk.mcProps.getting("fabric.api.key_mapping").get(), "fabric-resource-loader-v0").forEach {
|
||||
listOf("fabric-key-binding-api-v1", "fabric-resource-loader-v0").forEach {
|
||||
modTestmodImplementation(fabricApi.module(it, mcLibs.versions.fabric.api.get()))
|
||||
}
|
||||
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModElements"))
|
||||
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModApiElements"))
|
||||
testmodImplementation(project(":tweed5-bundle", configuration = "runtimeElements"))
|
||||
testmodImplementation(project(":tweed5-bundle-pojo-weaving", configuration = "runtimeElements"))
|
||||
testmodImplementation(project(":tweed5-fabric-helper", configuration = "namedElements"))
|
||||
modTestmodImplementation(mcLibs.coat)
|
||||
modTestmodImplementation(mcLibs.amecs.priorityKeyMappings)
|
||||
modTestmodImplementation(mcLibs.amecs.api)
|
||||
testmodImplementation("de.siphalor.tweed5:tweed5-serde-hjson")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatMapper;
|
||||
import de.siphalor.tweed5.coat.bridge.impl.TweedCoatMappersImpl;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -66,4 +67,10 @@ public class TweedCoatMappers {
|
||||
//noinspection unchecked
|
||||
return TweedCoatMappersImpl.convertingTextMapper(new Class[]{valueClass}, textMapper, textParser);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public static <T> TweedCoatMapper<T> serdeTextMapper(Class<T> valueClass) {
|
||||
//noinspection unchecked
|
||||
return TweedCoatMappersImpl.serdeTextMapper(new Class[]{valueClass});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssues;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||
import lombok.CustomLog;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.literalComponent;
|
||||
|
||||
@CustomLog
|
||||
@CommonsLog
|
||||
public class BasicTweedCoatEntryHandler<T extends @Nullable Object> implements ConfigEntryHandler<T> {
|
||||
protected final ConfigEntry<T> configEntry;
|
||||
protected final T defaultValue;
|
||||
|
||||
@@ -2,8 +2,8 @@ package de.siphalor.tweed5.coat.bridge.api.mapping.handler;
|
||||
|
||||
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||
import de.siphalor.coat.handler.Message;
|
||||
import lombok.CustomLog;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -16,7 +16,7 @@ import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.literalCo
|
||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@CustomLog
|
||||
@CommonsLog
|
||||
public class ConvertingTweedCoatEntryHandler<T extends @Nullable Object, C> implements ConfigEntryHandler<C> {
|
||||
private static final String CONVERSION_EXCEPTION_TEXT_KEY = "tweed5_coat_bridge.handler.conversion.exception";
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.siphalor.tweed5.coat.bridge.impl;
|
||||
|
||||
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||
import de.siphalor.coat.handler.Message;
|
||||
import de.siphalor.coat.input.CheckBoxConfigInput;
|
||||
import de.siphalor.coat.input.ConfigInput;
|
||||
import de.siphalor.coat.input.CycleButtonConfigInput;
|
||||
@@ -20,14 +21,24 @@ import de.siphalor.tweed5.coat.bridge.api.mapping.handler.BasicTweedCoatEntryHan
|
||||
import de.siphalor.tweed5.coat.bridge.api.mapping.handler.ConvertingTweedCoatEntryHandler;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import lombok.CustomLog;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.language.I18n;
|
||||
import net.minecraft.resources.Identifier;
|
||||
//- import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -35,10 +46,9 @@ import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponent;
|
||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
|
||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.*;
|
||||
|
||||
@CustomLog
|
||||
@CommonsLog
|
||||
@SuppressWarnings("unchecked")
|
||||
public class TweedCoatMappersImpl {
|
||||
public static TweedCoatMapper<Byte> BYTE_TEXT_MAPPER = convertingTextMapper(
|
||||
@@ -83,6 +93,10 @@ public class TweedCoatMappersImpl {
|
||||
|
||||
public static TweedCoatMapper<Object> COMPOUND_CATEGORY_MAPPER = new CompoundCategoryMapper<>();
|
||||
|
||||
public static <T> TweedCoatMapper<T> serdeTextMapper(Class<T>[] valueClasses) {
|
||||
return new SerdeTextMapper<>(valueClasses);
|
||||
}
|
||||
|
||||
public static <T> TweedCoatMapper<T> convertingTextMapper(
|
||||
Class<T>[] valueClasses,
|
||||
Function<T, String> textMapper,
|
||||
@@ -91,6 +105,240 @@ public class TweedCoatMappersImpl {
|
||||
return new ConvertingTextMapper<>(valueClasses, textMapper, textParser);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class SerdeTextMapper<T> implements TweedCoatMapper<T> {
|
||||
private final Class<T>[] valueClasses;
|
||||
|
||||
@Override
|
||||
public TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||
if (!anyClassMatches(entry.valueClass(), valueClasses)) {
|
||||
return TweedCoatEntryMappingResult.notApplicable();
|
||||
}
|
||||
|
||||
ReadWriteExtension readWriteExtension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalArgumentException("No ReadWriteExtension registered"));
|
||||
|
||||
return new TweedCoatEntryMappingResult<T, String>() {
|
||||
@Override
|
||||
public boolean isApplicable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigInput<String> createInput(TweedCoatEntryCreationContext<T> context) {
|
||||
return new TextConfigInput(convertToString(context.currentValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigEntryHandler<String> createHandler(TweedCoatEntryCreationContext<T> context) {
|
||||
if (context.parentSaveHandler() == null) {
|
||||
throw new IllegalArgumentException("No parent save handler provided");
|
||||
}
|
||||
return new ConfigEntryHandler<String>() {
|
||||
@Override
|
||||
public String getDefault() {
|
||||
return convertToString(context.defaultValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message> getMessages(String text) {
|
||||
try {
|
||||
TweedReadResult<T> readResult = convertFromString(text);
|
||||
if (readResult.hasValue() && !readResult.hasIssues()) {
|
||||
return Collections.emptyList();
|
||||
} else if (readResult.hasIssues()) {
|
||||
Message.Level messageLevel = readResult.hasValue()
|
||||
? Message.Level.WARNING
|
||||
: Message.Level.ERROR;
|
||||
return Arrays.stream(readResult.issues()).map(issue ->
|
||||
new Message(messageLevel, literalComponent(issue.exception().getMessage()))
|
||||
).collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return Collections.singletonList(
|
||||
new Message(Message.Level.ERROR, literalComponent(e.getMessage()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String s) {
|
||||
try {
|
||||
TweedReadResult<T> readResult = convertFromString(s);
|
||||
if (readResult.hasValue()) {
|
||||
if (readResult.hasIssues()) {
|
||||
log.warn(
|
||||
"There were issues understanding value \"" + s + "\":\n - "
|
||||
+ Arrays.stream(readResult.issues())
|
||||
.map(issue -> issue.exception().getMessage())
|
||||
.collect(Collectors.joining("\n - "))
|
||||
);
|
||||
}
|
||||
context.parentSaveHandler().accept(readResult.value());
|
||||
} else {
|
||||
log.error(
|
||||
"Failed to understand value \"" + s + "\":\n - "
|
||||
+ Arrays.stream(readResult.issues())
|
||||
.map(issue -> issue.exception().getMessage())
|
||||
.collect(Collectors.joining("\n - "))
|
||||
);
|
||||
context.parentSaveHandler().accept(context.defaultValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to save value \"" + s + "\"", e);
|
||||
context.parentSaveHandler().accept(context.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component asText(String text) {
|
||||
return literalComponent(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String convertToString(T value) {
|
||||
String[] wrapper = new String[]{""};
|
||||
try {
|
||||
readWriteExtension.write(
|
||||
new TweedDataVisitor() {
|
||||
@Override
|
||||
public void visitNull() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBoolean(boolean value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByte(byte value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShort(short value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(int value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLong(long value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFloat(float value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDouble(double value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(String value) {
|
||||
wrapper[0] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListEnd() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEntryKey(String key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEnd() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDecoration(TweedDataDecoration decoration) {
|
||||
}
|
||||
}, value, entry, readWriteExtension.createReadWriteContextExtensionsData()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to serialize value " + value + " to string", e);
|
||||
}
|
||||
return wrapper[0];
|
||||
}
|
||||
|
||||
private TweedReadResult<T> convertFromString(String text) {
|
||||
TweedEntryReader<T, ConfigEntry<T>> reader =
|
||||
readWriteExtension.getDefinedEntryReader(entry);
|
||||
Patchwork readExtData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||
//noinspection DataFlowIssue
|
||||
return reader.read(
|
||||
new TweedDataReader() {
|
||||
private boolean consumed = false;
|
||||
|
||||
@Override
|
||||
public TweedDataToken peekToken() throws TweedDataReadException {
|
||||
if (consumed) {
|
||||
throw new IllegalStateException("Already consumed");
|
||||
}
|
||||
return new TweedDataToken() {
|
||||
@Override
|
||||
public boolean canReadAsString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readAsString() {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedDataToken readToken() throws TweedDataReadException {
|
||||
TweedDataToken token = peekToken();
|
||||
consumed = true;
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}, entry, new TweedReadContext() {
|
||||
@Override
|
||||
public ReadWriteExtension readWriteExtension() {
|
||||
return readWriteExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Patchwork extensionsData() {
|
||||
return readExtData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> TweedReadResult<T> readSubEntry(
|
||||
TweedDataReader reader,
|
||||
C entry
|
||||
) {
|
||||
return TweedReadResult.empty();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class ConvertingTextMapper<T> implements TweedCoatMapper<T> {
|
||||
private final Class<T>[] valueClasses;
|
||||
@@ -256,20 +504,12 @@ public class TweedCoatMappersImpl {
|
||||
CompoundConfigEntry<T> compoundEntry = (CompoundConfigEntry<T>) entry;
|
||||
|
||||
Optional<AttributesExtension> attributesExtension = entry.container().extension(AttributesExtension.class);
|
||||
//# if MC_VERSION_NUMBER >= 260100
|
||||
Identifier backgroundTexture = attributesExtension
|
||||
//# else
|
||||
//- ResourceLocation backgroundTexture = attributesExtension
|
||||
//# end
|
||||
ResourceLocation backgroundTexture = attributesExtension
|
||||
.map(extension -> extension.getAttributeValue(
|
||||
entry,
|
||||
TweedCoatAttributes.BACKGROUND_TEXTURE
|
||||
))
|
||||
//# if MC_VERSION_NUMBER >= 260100
|
||||
.map(Identifier::tryParse)
|
||||
//# else
|
||||
//- .map(ResourceLocation::tryParse)
|
||||
//# end
|
||||
.map(ResourceLocation::tryParse)
|
||||
.orElse(null);
|
||||
String translationKey = attributesExtension
|
||||
.map(extension -> extension.getAttributeValue(
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
package de.siphalor.tweed5.coat.bridge.testmod;
|
||||
|
||||
import de.siphalor.amecs.priority_key_mappings.api.AmecsPriorityKeyMapping;
|
||||
import de.siphalor.amecs.api.PriorityKeyBinding;
|
||||
import de.siphalor.coat.screen.ConfigScreen;
|
||||
import de.siphalor.tweed5.coat.bridge.api.ConfigScreenCreateParams;
|
||||
import de.siphalor.tweed5.coat.bridge.api.TweedCoatBridgeExtension;
|
||||
import de.siphalor.tweed5.coat.bridge.api.TweedCoatMappers;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonSerde;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonSerde;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver;
|
||||
import lombok.CustomLog;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
//- import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
@@ -55,11 +54,7 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
|
||||
|
||||
config = configContainerHelper.loadAndUpdateInConfigDirectory(() -> DEFAULT_CONFIG_VALUE);
|
||||
|
||||
//# if MC_VERSION_NUMBER >= 260100
|
||||
KeyMappingHelper.registerKeyMapping(new ScreenKeyBinding(
|
||||
//# else
|
||||
//- KeyBindingHelper.registerKeyBinding(new ScreenKeyBinding(
|
||||
//# end
|
||||
KeyBindingHelper.registerKeyBinding(new ScreenKeyBinding(
|
||||
MOD_ID + ".config",
|
||||
GLFW.GLFW_KEY_T,
|
||||
//# if MC_VERSION_NUMBER >= 12109
|
||||
@@ -72,7 +67,7 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
|
||||
log.info("Current config: " + config);
|
||||
}
|
||||
|
||||
private class ScreenKeyBinding extends KeyMapping implements AmecsPriorityKeyMapping {
|
||||
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
|
||||
//# if MC_VERSION_NUMBER >= 12109
|
||||
public ScreenKeyBinding(String name, int key, Category category) {
|
||||
//# else
|
||||
|
||||
@@ -15,12 +15,11 @@ dependencies {
|
||||
compileOnly("de.siphalor.tweed5:tweed5-weaver-pojo-serde-extension")
|
||||
compileOnly("de.siphalor.tweed5:tweed5-weaver-pojo-validation-extension")
|
||||
compileOnly("de.siphalor.tweed5:tweed5-weaver-pojo-presets-extension")
|
||||
compileOnly(project(":tweed5-logging", configuration = "minecraftModApiElements"))
|
||||
|
||||
listOf("fabric-networking-api-v1", "fabric-lifecycle-events-v1").forEach {
|
||||
modTestmodImplementation(fabricApi.module(it, mcLibs.versions.fabric.api.get()))
|
||||
}
|
||||
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModElements"))
|
||||
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModApiElements"))
|
||||
testmodImplementation(project(":tweed5-bundle", configuration = "runtimeElements"))
|
||||
testmodImplementation(project(":tweed5-bundle-pojo-weaving", configuration = "runtimeElements"))
|
||||
testmodImplementation("de.siphalor.tweed5:tweed5-comment-loader-extension")
|
||||
|
||||
@@ -3,7 +3,7 @@ package de.siphalor.tweed5.fabric.helper.api;
|
||||
import de.siphalor.tweed5.annotationinheritance.api.AnnotationInheritance;
|
||||
import de.siphalor.tweed5.attributesextension.api.AttributesExtension;
|
||||
import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validationfallback.api.ValidationFallbackExtension;
|
||||
|
||||
@@ -4,16 +4,16 @@ import com.google.gson.stream.JsonReader;
|
||||
import de.siphalor.tweed5.commentloaderextension.api.CommentLoaderExtension;
|
||||
import de.siphalor.tweed5.commentloaderextension.api.CommentPathProcessor;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.data.gson.GsonReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde.gson.GsonReader;
|
||||
import lombok.Builder;
|
||||
import lombok.CustomLog;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
@CustomLog
|
||||
@CommonsLog
|
||||
@Builder
|
||||
public class FabricConfigCommentLoader {
|
||||
private final ConfigContainer<?> configContainer;
|
||||
|
||||
@@ -2,29 +2,29 @@ package de.siphalor.tweed5.fabric.helper.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedSerde;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import lombok.CustomLog;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadIssue;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedSerde;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@CustomLog
|
||||
@CommonsLog
|
||||
public class FabricConfigContainerHelper<T extends @Nullable Object> {
|
||||
@Getter
|
||||
private final ConfigContainer<T> configContainer;
|
||||
@@ -84,14 +84,24 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
|
||||
return;
|
||||
}
|
||||
|
||||
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
|
||||
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
|
||||
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||
readContextCustomizer.accept(contextExtensionsData);
|
||||
PatchInfo patchInfo = patchExtension.collectPatchInfo(contextExtensionsData);
|
||||
|
||||
T readValue = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData);
|
||||
TweedReadResult<T> readResult = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData);
|
||||
if (readResult.hasValue()) {
|
||||
patchExtension.patch(configContainer.rootEntry(), value, readResult.value(), patchInfo);
|
||||
|
||||
if (readResult.hasIssues()) {
|
||||
log.warn(formatIssuesLogMessage(configFile, "issues", readResult.issues()));
|
||||
}
|
||||
} else if (readResult.hasIssues()) {
|
||||
log.error(formatIssuesLogMessage(configFile, "errors", readResult.issues()));
|
||||
} else {
|
||||
log.debug("Reading config file " + configFile.getAbsolutePath() + " yielded empty result");
|
||||
}
|
||||
|
||||
patchExtension.patch(configContainer.rootEntry(), value, readValue, patchInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
||||
}
|
||||
@@ -108,20 +118,54 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
|
||||
return defaultValueSupplier.get();
|
||||
}
|
||||
|
||||
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
|
||||
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
|
||||
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||
return readWriteExtension.read(reader, configContainer.rootEntry(), contextExtensionsData);
|
||||
TweedReadResult<T> readResult = readWriteExtension.read(
|
||||
reader,
|
||||
configContainer.rootEntry(),
|
||||
contextExtensionsData
|
||||
);
|
||||
if (readResult.hasValue()) {
|
||||
if (readResult.hasIssues()) {
|
||||
log.warn(formatIssuesLogMessage(configFile, "issues", readResult.issues()));
|
||||
}
|
||||
return readResult.value();
|
||||
} else if (readResult.hasIssues()) {
|
||||
log.error(formatIssuesLogMessage(configFile, "errors", readResult.issues()));
|
||||
} else {
|
||||
log.info(
|
||||
"Reading config file "
|
||||
+ configFile.getAbsolutePath()
|
||||
+ " yielded empty result, using default value"
|
||||
);
|
||||
}
|
||||
return defaultValueSupplier.get();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
||||
return defaultValueSupplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
private String formatIssuesLogMessage(File file, String type, TweedReadIssue[] issues) {
|
||||
String filePath = file.getAbsolutePath();
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder(20 + filePath.length() + type.length() + issues.length * 50);
|
||||
stringBuilder.append("Encountered ");
|
||||
stringBuilder.append(type);
|
||||
stringBuilder.append(" while reading ");
|
||||
stringBuilder.append(filePath);
|
||||
stringBuilder.append(": ");
|
||||
for (TweedReadIssue issue : issues) {
|
||||
stringBuilder.append(" - ").append(issue).append("\n");
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public void writeConfigInConfigDirectory(T configValue) {
|
||||
File configFile = getConfigFile();
|
||||
Path tempConfigDirectory = getOrCreateTempConfigDirectory();
|
||||
File tempConfigFile = tempConfigDirectory.resolve(getConfigFileName()).toFile();
|
||||
try (TweedDataWriter writer = serde.createWriter(new FileOutputStream(tempConfigFile))) {
|
||||
try (TweedDataWriter writer = serde.createWriter(Files.newOutputStream(tempConfigFile.toPath()))) {
|
||||
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||
|
||||
readWriteExtension.write(
|
||||
|
||||
@@ -2,8 +2,8 @@ package de.siphalor.tweed5.fabric.helper.testmod;
|
||||
|
||||
import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonSerde;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonSerde;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.fabric.helper.api.FabricConfigCommentLoader;
|
||||
import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
org.gradle.jvmargs = -Xmx2G
|
||||
org.gradle.configuration-cache = true
|
||||
|
||||
minecraft.version.descriptor = 26.1.0
|
||||
minecraft.version.descriptor = 1.21.10
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 11605
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.42.0+1.16"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "8"
|
||||
minecraft = "1.16.5"
|
||||
parchment = "2022.03.06"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.16.5", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.16.5", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.16.5", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 11701
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.46.1+1.17"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "16"
|
||||
minecraft = "1.17.1"
|
||||
parchment = "2021.12.12"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.17.1", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.17.1", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.17.1", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 11802
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.77.0+1.18.2"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "17"
|
||||
minecraft = "1.18.2"
|
||||
parchment = "2022.11.06"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.18.2", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.18.2", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.18.2", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 11904
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.87.2+1.19.4"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "17"
|
||||
minecraft = "1.19.4"
|
||||
parchment = "2023.06.26"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.19.4", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.19.4", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.19.4", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 12006
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.100.8+1.20.6"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "17"
|
||||
minecraft = "1.20.6"
|
||||
parchment = "2024.06.16"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.20.2", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.20.2", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.20.5", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +1 @@
|
||||
fabric.api.key_mapping = fabric-key-binding-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 12110
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
amecs-api = "1.6.2"
|
||||
coat = "1.0.0-beta.24"
|
||||
fabric-api = "0.136.0+1.21.10"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "21"
|
||||
minecraft = "1.21.10"
|
||||
parchment = "2025.10.12"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom-remap", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.21.9", version.ref = "amecs-priorityKeyMappings" }
|
||||
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.21.9", version.ref = "amecs-api" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.21.10", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,3 +0,0 @@
|
||||
fabric.api.key_mapping = fabric-key-mapping-api-v1
|
||||
|
||||
preprocessor.mc_version_number = 260100
|
||||
@@ -1,16 +0,0 @@
|
||||
[versions]
|
||||
amecs-priorityKeyMappings = "1.0.1"
|
||||
coat = "1.1.1"
|
||||
fabric-api = "0.144.3+26.1"
|
||||
fabric-loom = "1.15-SNAPSHOT"
|
||||
java = "25"
|
||||
minecraft = "26.1"
|
||||
|
||||
[plugins]
|
||||
fabric-loom = { id = "net.fabricmc.fabric-loom", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc26.1.0", version.ref = "amecs-priorityKeyMappings" }
|
||||
coat = { group = "de.siphalor.coat", name = "coat-mc26.1.0", version.ref = "coat" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||
@@ -1,11 +1,11 @@
|
||||
[versions]
|
||||
fabric-loader = "0.18.5"
|
||||
jcyo = "0.5.1"
|
||||
smcmtk = "0.1.0"
|
||||
fabric-loader = "0.17.2"
|
||||
fabric-loom = "1.11-SNAPSHOT"
|
||||
jcyo = "0.6.1"
|
||||
|
||||
[plugins]
|
||||
jcyo = { id = "de.siphalor.jcyo", version.ref = "jcyo" }
|
||||
smcmtk = { id = "de.siphalor.minecraft-modding-toolkit.project-plugin", version.ref = "smcmtk" }
|
||||
fabric-loom = { id = "fabric-loom", version.ref = "fabric-loom" }
|
||||
|
||||
[libraries]
|
||||
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
lombok.accessors.fluent = true
|
||||
lombok.addLombokGeneratedAnnotation = true
|
||||
|
||||
# Special configuration, so that the testmod can use the correct shadowed logging classes.
|
||||
lombok.log.apacheCommons.flagUsage=WARNING
|
||||
lombok.log.custom.declaration=de.siphalor.tweed5.shadowed.org.apache.commons.logging.Log de.siphalor.tweed5.shadowed.org.apache.commons.logging.LogFactory.getLog(TYPE)
|
||||
lombok.log.custom.flagUsage=ALLOW
|
||||
lombok.addLombokGeneratedAnnotation = true
|
||||
4
tweed5-minecraft/lombok.testmod.config
Normal file
4
tweed5-minecraft/lombok.testmod.config
Normal file
@@ -0,0 +1,4 @@
|
||||
# Special configuration, so that the testmod can use the correct shadowed logging classes.
|
||||
lombok.log.apacheCommons.flagUsage=WARNING
|
||||
lombok.log.custom.declaration=de.siphalor.tweed5.shadowed.org.apache.commons.logging.Log de.siphalor.tweed5.shadowed.org.apache.commons.logging.LogFactory.getLog(TYPE)
|
||||
lombok.log.custom.flagUsage=ALLOW
|
||||
13
tweed5-minecraft/networking/build.gradle.kts
Normal file
13
tweed5-minecraft/networking/build.gradle.kts
Normal file
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("de.siphalor.tweed5.local-runtime-only")
|
||||
id("de.siphalor.tweed5.minecraft.mod.cross-version")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modCompileOnly(fabricApi.module("fabric-networking-api-v1", mcLibs.versions.fabric.api.get()))
|
||||
compileOnly("de.siphalor.tweed5:tweed5-core")
|
||||
compileOnly("de.siphalor.tweed5:tweed5-serde-extension")
|
||||
|
||||
testImplementation("de.siphalor.tweed5:tweed5-core")
|
||||
testImplementation("de.siphalor.tweed5:tweed5-serde-extension")
|
||||
}
|
||||
2
tweed5-minecraft/networking/gradle.properties
Normal file
2
tweed5-minecraft/networking/gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
module.name = Tweed 5 Netwoking
|
||||
module.description = Minecraft networking support for Tweed 5
|
||||
@@ -0,0 +1,297 @@
|
||||
package de.siphalor.tweed5.minecraft.networking.api;
|
||||
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataTokens;
|
||||
import de.siphalor.tweed5.minecraft.networking.impl.ByteBufSerdeConstants;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ByteBufReader implements TweedDataReader {
|
||||
private final ByteBuf buf;
|
||||
private final Deque<Context> contextStack = new ArrayDeque<>();
|
||||
private @Nullable TweedDataToken peek;
|
||||
|
||||
@Override
|
||||
public TweedDataToken peekToken() throws TweedDataReadException {
|
||||
if (peek != null) return peek;
|
||||
ensureReadable();
|
||||
return (peek = nextToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedDataToken readToken() throws TweedDataReadException {
|
||||
if (peek != null) {
|
||||
TweedDataToken token = peek;
|
||||
peek = null;
|
||||
return token;
|
||||
}
|
||||
ensureReadable();
|
||||
return nextToken();
|
||||
}
|
||||
|
||||
private void ensureReadable() throws TweedDataReadException {
|
||||
if (!buf.isReadable()) {
|
||||
throw TweedDataReadException.builder().message("Reached end of buffer").build();
|
||||
}
|
||||
}
|
||||
|
||||
private TweedDataToken nextToken() throws TweedDataReadException {
|
||||
int b = Byte.toUnsignedInt(buf.readByte());
|
||||
switch (b) {
|
||||
case ByteBufSerdeConstants.NULL_VALUE:
|
||||
return wrapTokenForContext(TweedDataTokens.getNull());
|
||||
case ByteBufSerdeConstants.FALSE_VALUE:
|
||||
return wrapTokenForContext(BooleanToken.FALSE);
|
||||
case ByteBufSerdeConstants.TRUE_VALUE:
|
||||
return wrapTokenForContext(BooleanToken.TRUE);
|
||||
case ByteBufSerdeConstants.EMPTY_STRING_VALUE:
|
||||
return wrapTokenForContext(new StringToken(""));
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_INT8:
|
||||
return wrapTokenForContext(new ByteToken(buf.readByte()));
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_INT16:
|
||||
return wrapTokenForContext(new IntToken(buf.readShort()));
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_INT32:
|
||||
return wrapTokenForContext(new IntToken(buf.readInt()));
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_INT64: {
|
||||
long value = buf.readLong();
|
||||
if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
|
||||
return wrapTokenForContext(new IntToken((int) value));
|
||||
} else {
|
||||
return wrapTokenForContext(new TweedDataToken() {
|
||||
@Override
|
||||
public boolean canReadAsLong() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readAsLong() {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_FLOAT:
|
||||
return wrapTokenForContext(new TweedDataToken() {
|
||||
private final float value = buf.readFloat();
|
||||
@Override
|
||||
public boolean canReadAsFloat() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readAsFloat() {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
case ByteBufSerdeConstants.VARNUM_VARIANT_DOUBLE:
|
||||
return wrapTokenForContext(new TweedDataToken() {
|
||||
private final double value = buf.readDouble();
|
||||
@Override
|
||||
public boolean canReadAsDouble() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readAsDouble() {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
case ByteBufSerdeConstants.COMPLEX_VARIANT_STRING: {
|
||||
int length = buf.readInt();
|
||||
ByteBuf byteBuf = buf.readBytes(length);
|
||||
return wrapTokenForContext(new StringToken(byteBuf.toString(StandardCharsets.UTF_8)));
|
||||
}
|
||||
case ByteBufSerdeConstants.COMPLEX_VARIANT_LIST: {
|
||||
TweedDataToken token = wrapTokenForContext(TweedDataTokens.getListStart(), false);
|
||||
contextStack.push(Context.LIST);
|
||||
return token;
|
||||
}
|
||||
case ByteBufSerdeConstants.COMPLEX_VARIANT_MAP: {
|
||||
TweedDataToken token = wrapTokenForContext(TweedDataTokens.getMapStart(), false);
|
||||
contextStack.push(Context.MAP);
|
||||
return token;
|
||||
}
|
||||
case ByteBufSerdeConstants.COMPLEX_VARIANT_END: {
|
||||
Context context = contextStack.pop();
|
||||
return wrapTokenForContext(
|
||||
context == Context.MAP
|
||||
? TweedDataTokens.getMapEnd()
|
||||
: TweedDataTokens.getListEnd()
|
||||
);
|
||||
}
|
||||
default:
|
||||
int specialEmbedType = b & ByteBufSerdeConstants.SPECIAL_EMBED_TYPE_MASK;
|
||||
if (specialEmbedType == ByteBufSerdeConstants.UINT6_TYPE) {
|
||||
return wrapTokenForContext(new ByteToken((byte) (b & ByteBufSerdeConstants.SPECIAL_EMBED_VALUE_MASK)));
|
||||
} else if (specialEmbedType == ByteBufSerdeConstants.SMALL_STRING_TYPE) {
|
||||
int length = (b & ByteBufSerdeConstants.SPECIAL_EMBED_VALUE_MASK) + 1;
|
||||
ByteBuf byteBuf = buf.readBytes(length);
|
||||
return wrapTokenForContext(new StringToken(byteBuf.toString(StandardCharsets.UTF_8)));
|
||||
}
|
||||
throw TweedDataReadException.builder()
|
||||
.message("Unknown type byte value " + Integer.toBinaryString(b))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private TweedDataToken wrapTokenForContext(TweedDataToken token) {
|
||||
return wrapTokenForContext(token, true);
|
||||
}
|
||||
|
||||
private TweedDataToken wrapTokenForContext(TweedDataToken token, boolean isValueEnd) {
|
||||
Context context = contextStack.peek();
|
||||
if (context == null) {
|
||||
return token;
|
||||
} else if (context == Context.LIST) {
|
||||
return TweedDataTokens.asListValue(token);
|
||||
} else if (context == Context.MAP) {
|
||||
contextStack.push(Context.MAP_VALUE);
|
||||
return TweedDataTokens.asMapEntryKey(token);
|
||||
} else {
|
||||
if (isValueEnd) {
|
||||
contextStack.pop();
|
||||
}
|
||||
return TweedDataTokens.asMapEntryValue(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
private enum Context {
|
||||
LIST, MAP, MAP_VALUE
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class BooleanToken implements TweedDataToken {
|
||||
public static final BooleanToken FALSE = new BooleanToken(false);
|
||||
public static final BooleanToken TRUE = new BooleanToken(true);
|
||||
|
||||
private final boolean value;
|
||||
|
||||
@Override
|
||||
public boolean canReadAsBoolean() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readAsBoolean() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ByteToken implements TweedDataToken {
|
||||
private final byte value;
|
||||
|
||||
@Override
|
||||
public boolean canReadAsByte() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readAsByte() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsShort() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readAsShort() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsInt() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readAsInt() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsLong() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readAsLong() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class IntToken implements TweedDataToken {
|
||||
private final int value;
|
||||
|
||||
@Override
|
||||
public boolean canReadAsByte() {
|
||||
return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readAsByte() {
|
||||
return (byte) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsShort() {
|
||||
return value >= Short.MIN_VALUE && value <= Short.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readAsShort() {
|
||||
return (short) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsInt() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readAsInt() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadAsLong() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readAsLong() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class StringToken implements TweedDataToken {
|
||||
private final String value;
|
||||
|
||||
@Override
|
||||
public boolean canReadAsString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readAsString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package de.siphalor.tweed5.minecraft.networking.api;
|
||||
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.minecraft.networking.impl.ByteBufSerdeConstants;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RawByteBufWriter implements TweedDataWriter {
|
||||
protected final ByteBuf buf;
|
||||
|
||||
@Override
|
||||
public void visitNull() {
|
||||
buf.writeByte(ByteBufSerdeConstants.NULL_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBoolean(boolean value) {
|
||||
if (value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.TRUE_VALUE);
|
||||
} else {
|
||||
buf.writeByte(ByteBufSerdeConstants.FALSE_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByte(byte value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT8);
|
||||
buf.writeByte(Byte.toUnsignedInt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShort(short value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT16);
|
||||
buf.writeShort(Short.toUnsignedInt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(int value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT32);
|
||||
buf.writeInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLong(long value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT64);
|
||||
buf.writeLong(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFloat(float value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_FLOAT);
|
||||
buf.writeFloat(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDouble(double value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_DOUBLE);
|
||||
buf.writeDouble(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(String value) {
|
||||
writeStringBytes(value.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
protected void writeStringBytes(byte[] bytes) {
|
||||
buf.writeByte(ByteBufSerdeConstants.COMPLEX_VARIANT_STRING);
|
||||
buf.writeInt(bytes.length);
|
||||
buf.writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListStart() {
|
||||
buf.writeByte(ByteBufSerdeConstants.COMPLEX_VARIANT_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitListEnd() {
|
||||
buf.writeByte(ByteBufSerdeConstants.COMPLEX_VARIANT_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapStart() {
|
||||
buf.writeByte(ByteBufSerdeConstants.COMPLEX_VARIANT_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEntryKey(String key) {
|
||||
visitString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEnd() {
|
||||
buf.writeByte(ByteBufSerdeConstants.COMPLEX_VARIANT_END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDecoration(TweedDataDecoration decoration) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package de.siphalor.tweed5.minecraft.networking.api;
|
||||
|
||||
import de.siphalor.tweed5.minecraft.networking.impl.ByteBufSerdeConstants;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SlightlyCompressedByteBufWriter extends RawByteBufWriter {
|
||||
public SlightlyCompressedByteBufWriter(ByteBuf buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByte(byte value) {
|
||||
int v = Byte.toUnsignedInt(value);
|
||||
if (v <= 0b11_1111) {
|
||||
buf.writeByte(ByteBufSerdeConstants.UINT6_TYPE + (v & 0b11_1111));
|
||||
} else {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT8);
|
||||
buf.writeByte(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShort(short value) {
|
||||
int v = Short.toUnsignedInt(value);
|
||||
if (v <= 0b11_1111) {
|
||||
writeUint6(v);
|
||||
} else if (v <= 0b0111_1111) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT8);
|
||||
buf.writeByte(v & 0b0111_1111);
|
||||
} else if (v >= 0b1000_0000_0000_0000 && v <= 0b1000_0000_0111_1111) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT8);
|
||||
buf.writeByte(v & 0b0111_1111 | 0b1000_0000);
|
||||
} else {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT16);
|
||||
buf.writeShort(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(int value) {
|
||||
if (value >= 0 && value <= 0b11_1111) {
|
||||
writeUint6(value);
|
||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT8);
|
||||
if (value < 0) value |= 0b1000_0000;
|
||||
buf.writeByte(value);
|
||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT16);
|
||||
if (value < 0) value |= 0b1000_0000_0000_0000;
|
||||
buf.writeShort(value);
|
||||
} else {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT32);
|
||||
buf.writeInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLong(long value) {
|
||||
if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
|
||||
visitInt((int) value);
|
||||
} else {
|
||||
buf.writeByte(ByteBufSerdeConstants.VARNUM_VARIANT_INT64);
|
||||
buf.writeLong(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeUint6(int value) {
|
||||
buf.writeByte(ByteBufSerdeConstants.UINT6_TYPE | (value & 0b11_1111));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(String value) {
|
||||
if (value.isEmpty()) {
|
||||
buf.writeByte(ByteBufSerdeConstants.EMPTY_STRING_VALUE);
|
||||
return;
|
||||
}
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
if (bytes.length <= 0b100_0000) {
|
||||
buf.writeByte(ByteBufSerdeConstants.SMALL_STRING_TYPE | (bytes.length - 1));
|
||||
buf.writeBytes(bytes);
|
||||
} else {
|
||||
writeStringBytes(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.dataapi.api.decoration;
|
||||
package de.siphalor.tweed5.minecraft.networking.api;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.siphalor.tweed5.minecraft.networking.impl;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
|
||||
public class ByteBufSerdeConstants {
|
||||
public static final int TYPE_MASK = 0b1111_0000;
|
||||
public static final int VALUE_MASK = 0b0000_1111;
|
||||
|
||||
public static final int CONST_TYPE = 0;
|
||||
public static final int NULL_VALUE = 0;
|
||||
public static final int FALSE_VALUE = 1;
|
||||
public static final int TRUE_VALUE = 0b10;
|
||||
public static final int EMPTY_STRING_VALUE = 0b11;
|
||||
|
||||
public static final int VARNUM_TYPE = 0b0001_0000;
|
||||
public static final int VARNUM_VARIANT_INT8 = VARNUM_TYPE;
|
||||
public static final int VARNUM_VARIANT_INT16 = VARNUM_TYPE | 0b0001;
|
||||
public static final int VARNUM_VARIANT_INT32 = VARNUM_TYPE | 0b0010;
|
||||
public static final int VARNUM_VARIANT_INT64 = VARNUM_TYPE | 0b0011;
|
||||
public static final int VARNUM_VARIANT_FLOAT = VARNUM_TYPE | 0b1000;
|
||||
public static final int VARNUM_VARIANT_DOUBLE = VARNUM_TYPE | 0b1001;
|
||||
|
||||
public static final int COMPLEX_TYPE = 0b0010_0000;
|
||||
public static final int COMPLEX_VARIANT_STRING = COMPLEX_TYPE;
|
||||
public static final int COMPLEX_VARIANT_LIST = COMPLEX_TYPE | 0b0001;
|
||||
public static final int COMPLEX_VARIANT_MAP = COMPLEX_TYPE | 0b0010;
|
||||
public static final int COMPLEX_VARIANT_END = COMPLEX_TYPE | 0b1111;
|
||||
|
||||
// 0b01xx_xxxx is reserved for future use
|
||||
|
||||
public static final int SPECIAL_EMBED_TYPE_MASK = 0b1100_0000;
|
||||
public static final int SPECIAL_EMBED_VALUE_MASK = 0b0011_1111;
|
||||
public static final int UINT6_TYPE = 0b1000_0000;
|
||||
public static final int SMALL_STRING_TYPE = 0b1100_0000;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.minecraft.networking.impl;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,157 @@
|
||||
package de.siphalor.tweed5.minecraft.network.api;
|
||||
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.minecraft.networking.api.ByteBufReader;
|
||||
import de.siphalor.tweed5.minecraft.networking.api.RawByteBufWriter;
|
||||
import de.siphalor.tweed5.minecraft.networking.api.SlightlyCompressedByteBufWriter;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.params.provider.Arguments.argumentSet;
|
||||
|
||||
public class ByteBufReaderWriterTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("testParams")
|
||||
@SneakyThrows
|
||||
void test(Function<ByteBuf, TweedDataWriter> writerConstructor) {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
|
||||
try (TweedDataWriter writer = writerConstructor.apply(buffer)) {
|
||||
writer.visitMapStart();
|
||||
writer.visitMapEntryKey("first");
|
||||
writer.visitNull();
|
||||
writer.visitMapEntryKey("kind-of a_weird/key!");
|
||||
writer.visitListStart();
|
||||
writer.visitByte((byte) 12);
|
||||
writer.visitByte((byte) -12);
|
||||
writer.visitByte((byte) 123);
|
||||
writer.visitListEnd();
|
||||
writer.visitMapEntryKey("nums");
|
||||
writer.visitListStart();
|
||||
writer.visitShort((short) 1234);
|
||||
writer.visitInt(4321);
|
||||
writer.visitInt(Integer.MAX_VALUE);
|
||||
writer.visitLong(Long.MAX_VALUE);
|
||||
writer.visitFloat(1234.5678f);
|
||||
writer.visitDouble(1234.5678);
|
||||
writer.visitListEnd();
|
||||
writer.visitMapEntryKey("other");
|
||||
writer.visitString("Hello World!");
|
||||
writer.visitMapEnd();
|
||||
}
|
||||
|
||||
System.out.println("Buffer size is: " + buffer.writerIndex());
|
||||
assertThat(buffer.readerIndex()).isZero();
|
||||
|
||||
try (ByteBufReader reader = new ByteBufReader(buffer)) {
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isMapStart).isEqualTo(true);
|
||||
assertNextMapKey(reader.readToken(), "first");
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isNull).isEqualTo(true);
|
||||
assertNextMapKey(reader.readToken(), "kind-of a_weird/key!");
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isListStart).isEqualTo(true);
|
||||
assertByteToken(reader.readToken(), (byte) 12);
|
||||
assertByteToken(reader.readToken(), (byte) -12);
|
||||
assertByteToken(reader.readToken(), (byte) 123);
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isListEnd).isEqualTo(true);
|
||||
assertNextMapKey(reader.readToken(), "nums");
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isListStart).isEqualTo(true);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsByte()).isFalse(),
|
||||
token -> assertThat(token.canReadAsShort()).isTrue(),
|
||||
token -> assertThat(token.readAsShort()).isEqualTo((short) 1234),
|
||||
token -> assertThat(token.canReadAsInt()).isTrue(),
|
||||
token -> assertThat(token.readAsInt()).isEqualTo(1234),
|
||||
token -> assertThat(token.canReadAsLong()).isTrue(),
|
||||
token -> assertThat(token.readAsLong()).isEqualTo(1234L)
|
||||
);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsByte()).isFalse(),
|
||||
token -> assertThat(token.canReadAsShort()).isTrue(),
|
||||
token -> assertThat(token.readAsShort()).isEqualTo((short) 4321),
|
||||
token -> assertThat(token.canReadAsInt()).isTrue(),
|
||||
token -> assertThat(token.readAsInt()).isEqualTo(4321),
|
||||
token -> assertThat(token.canReadAsLong()).isTrue(),
|
||||
token -> assertThat(token.readAsLong()).isEqualTo(4321L)
|
||||
);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsByte()).isFalse(),
|
||||
token -> assertThat(token.canReadAsShort()).isFalse(),
|
||||
token -> assertThat(token.canReadAsInt()).isTrue(),
|
||||
token -> assertThat(token.readAsInt()).isEqualTo(Integer.MAX_VALUE),
|
||||
token -> assertThat(token.canReadAsLong()).isTrue(),
|
||||
token -> assertThat(token.readAsLong()).isEqualTo(Integer.MAX_VALUE)
|
||||
);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsByte()).isFalse(),
|
||||
token -> assertThat(token.canReadAsShort()).isFalse(),
|
||||
token -> assertThat(token.canReadAsInt()).isFalse(),
|
||||
token -> assertThat(token.canReadAsLong()).isTrue(),
|
||||
token -> assertThat(token.readAsLong()).isEqualTo(Long.MAX_VALUE)
|
||||
);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsFloat()).isTrue(),
|
||||
token -> assertThat(token.readAsFloat()).isEqualTo(1234.5678f)
|
||||
);
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsDouble()).isTrue(),
|
||||
token -> assertThat(token.readAsDouble()).isEqualTo(1234.5678)
|
||||
);
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isListEnd).isEqualTo(true);
|
||||
assertNextMapKey(reader.readToken(), "other");
|
||||
assertThat(reader.readToken()).satisfies(
|
||||
token -> assertThat(token.canReadAsString()).isTrue(),
|
||||
token -> assertThat(token.readAsString()).isEqualTo("Hello World!"),
|
||||
token -> assertThat(token.isMapEntryValue()).isTrue()
|
||||
);
|
||||
assertThat(reader.readToken()).extracting(TweedDataToken::isMapEnd).isEqualTo(true);
|
||||
assertThatThrownBy(reader::readToken).isInstanceOf(TweedDataReadException.class);
|
||||
}
|
||||
|
||||
buffer.release();
|
||||
}
|
||||
|
||||
private void assertNextMapKey(TweedDataToken dataToken, String key) {
|
||||
assertThat(dataToken).satisfies(
|
||||
token -> assertThat(token.isMapEntryKey()).isTrue(),
|
||||
token -> assertThat(token.canReadAsString()).isTrue(),
|
||||
token -> assertThat(token.readAsString()).isEqualTo(key)
|
||||
);
|
||||
}
|
||||
|
||||
private void assertByteToken(TweedDataToken dataToken, byte value) {
|
||||
assertThat(dataToken).satisfies(
|
||||
token -> assertThat(token.canReadAsByte()).isTrue(),
|
||||
token -> assertThat(token.readAsByte()).isEqualTo(value),
|
||||
token -> assertThat(token.canReadAsShort()).isTrue(),
|
||||
token -> assertThat(token.readAsShort()).isEqualTo(value),
|
||||
token -> assertThat(token.canReadAsInt()).isTrue(),
|
||||
token -> assertThat(token.readAsInt()).isEqualTo(value),
|
||||
token -> assertThat(token.canReadAsLong()).isTrue(),
|
||||
token -> assertThat(token.readAsLong()).isEqualTo(value)
|
||||
);
|
||||
}
|
||||
|
||||
static Stream<Arguments> testParams() {
|
||||
return Stream.of(
|
||||
argumentSet(
|
||||
RawByteBufWriter.class.getSimpleName(),
|
||||
((Function<ByteBuf, ?>) RawByteBufWriter::new)
|
||||
),
|
||||
argumentSet(
|
||||
SlightlyCompressedByteBufWriter.class.getSimpleName(),
|
||||
((Function<ByteBuf, ?>) SlightlyCompressedByteBufWriter::new)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ dependencyResolutionManagement {
|
||||
}
|
||||
create("mcLibs") {
|
||||
val mcVersionDescriptor = providers.gradleProperty("minecraft.version.descriptor").get()
|
||||
from(files("gradle/mc-$mcVersionDescriptor/mc.versions.toml"))
|
||||
from(files("gradle/mc-$mcVersionDescriptor/mcLibs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ includeNormalModule("bundle-pojo-weaving")
|
||||
includeNormalModule("coat-bridge")
|
||||
includeNormalModule("fabric-helper")
|
||||
includeNormalModule("logging")
|
||||
includeNormalModule("networking")
|
||||
|
||||
fun includeNormalModule(name: String) {
|
||||
includeAs("tweed5-$name", name)
|
||||
|
||||
@@ -6,6 +6,7 @@ plugins {
|
||||
dependencies {
|
||||
implementation(project(":tweed5-core"))
|
||||
compileOnly(project(":tweed5-serde-extension"))
|
||||
testImplementation(project(":tweed5-default-extensions"))
|
||||
testImplementation(project(":tweed5-serde-extension"))
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
|
||||
@@ -9,17 +9,20 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReadException;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||
import de.siphalor.tweed5.dataapi.api.*;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReaderMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.WriterMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.extension.impl.TweedEntryReaderWriterImpls;
|
||||
import de.siphalor.tweed5.serde_api.api.DelegatingTweedDataWriter;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataUnsupportedValueException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.utils.api.UniqueSymbol;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -38,6 +41,8 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
private static final Set<String> MIDDLEWARES_MUST_COME_AFTER = Collections.emptySet();
|
||||
private static final UniqueSymbol TWEED_DATA_NOTHING_VALUE = new UniqueSymbol("nothing (skip value)");
|
||||
|
||||
public static final Object NOOP_MARKER = new Object();
|
||||
|
||||
private final ConfigContainer<?> configContainer;
|
||||
private @Nullable AttributesExtension attributesExtension;
|
||||
private final Set<String> filterableAttributes = new HashSet<>();
|
||||
@@ -155,7 +160,7 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
}
|
||||
}
|
||||
|
||||
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
|
||||
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -172,32 +177,28 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
|
||||
assert readWriteContextDataAccess != null;
|
||||
//noinspection unchecked
|
||||
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted
|
||||
= (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
|
||||
return new TweedEntryReader<@Nullable Object, ConfigEntry<Object>>() {
|
||||
@Override
|
||||
public @Nullable Object read(
|
||||
TweedDataReader reader,
|
||||
ConfigEntry<Object> entry,
|
||||
TweedReadContext context
|
||||
) throws TweedEntryReadException {
|
||||
ReadWriteContextCustomData contextData = context.extensionsData().get(readWriteContextDataAccess);
|
||||
if (contextData == null || doFiltersMatch(entry, contextData)) {
|
||||
return innerCasted.read(reader, entry, context);
|
||||
}
|
||||
TweedEntryReaderWriterImpls.NOOP_READER_WRITER.read(reader, entry, context);
|
||||
// TODO: this should result in a noop instead of a null value
|
||||
return null;
|
||||
return (TweedEntryReader<Object, ConfigEntry<Object>>) (reader, entry, readContext) -> {
|
||||
ReadWriteContextCustomData contextData = readContext.extensionsData().get(readWriteContextDataAccess);
|
||||
if (contextData == null) {
|
||||
contextData = new ReadWriteContextCustomData();
|
||||
readContext.extensionsData().set(readWriteContextDataAccess, contextData);
|
||||
}
|
||||
if (!doFiltersMatch(entry, contextData)) {
|
||||
TweedEntryReaderWriterImpls.NOOP_READER_WRITER.read(reader, entry, readContext);
|
||||
return TweedReadResult.empty();
|
||||
}
|
||||
return innerCasted.read(reader, entry, readContext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class WriterMiddleware implements Middleware<TweedEntryWriter<?, ?>> {
|
||||
private class WriterMiddleware implements Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext> {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -214,18 +215,18 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
|
||||
assert readWriteContextDataAccess != null;
|
||||
//noinspection unchecked
|
||||
TweedEntryWriter<Object, ConfigEntry<Object>> innerCasted
|
||||
= (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
|
||||
return (TweedEntryWriter<@Nullable Object, @NonNull ConfigEntry<@Nullable Object>>)
|
||||
(writer, value, entry, context) -> {
|
||||
ReadWriteContextCustomData contextData = context.extensionsData()
|
||||
(writer, value, entry, writeContext) -> {
|
||||
ReadWriteContextCustomData contextData = writeContext.extensionsData()
|
||||
.get(readWriteContextDataAccess);
|
||||
if (contextData == null || contextData.attributeFilters().isEmpty()) {
|
||||
innerCasted.write(writer, value, entry, context);
|
||||
innerCasted.write(writer, value, entry, writeContext);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,7 +236,7 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
}
|
||||
|
||||
if (doFiltersMatch(entry, contextData)) {
|
||||
innerCasted.write(writer, value, entry, context);
|
||||
innerCasted.write(writer, value, entry, writeContext);
|
||||
} else {
|
||||
try {
|
||||
writer.visitValue(TWEED_DATA_NOTHING_VALUE);
|
||||
@@ -380,5 +381,6 @@ public class AttributesReadWriteFilterExtensionImpl
|
||||
private static class ReadWriteContextCustomData {
|
||||
private final Map<String, Set<String>> attributeFilters = new HashMap<>();
|
||||
private boolean writerInstalled;
|
||||
private boolean noopHandlerInstalled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -26,9 +27,9 @@ import java.util.Map;
|
||||
|
||||
import static de.siphalor.tweed5.attributesextension.api.AttributesExtension.attribute;
|
||||
import static de.siphalor.tweed5.attributesextension.api.AttributesExtension.attributeDefault;
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.stringReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.stringReaderWriter;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -164,19 +165,21 @@ class AttributesReadWriteFilterExtensionImplTest {
|
||||
}
|
||||
}
|
||||
""")));
|
||||
Map<String, Object> readValue = configContainer.rootEntry().call(read(
|
||||
TweedReadResult<Map<String, Object>> readResult = configContainer.rootEntry().call(read(
|
||||
reader,
|
||||
patchwork -> attributesReadWriteFilterExtension.addFilter(patchwork, "type", "a")
|
||||
));
|
||||
|
||||
assertThat(readValue)
|
||||
assertThat(readResult)
|
||||
.extracting(TweedReadResult::value)
|
||||
.asInstanceOf(map(String.class, Object.class))
|
||||
.containsEntry("first", "1st")
|
||||
.containsEntry("second", null)
|
||||
.doesNotContainKey("second")
|
||||
.hasEntrySatisfying(
|
||||
"nested", nested -> assertThat(nested)
|
||||
.asInstanceOf(map(String.class, Object.class))
|
||||
.containsEntry("first", "n 1st")
|
||||
.containsEntry("second", null)
|
||||
.doesNotContainKey("second")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.siphalor.tweed5.attributesextension.impl.serde.filter;
|
||||
|
||||
import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension;
|
||||
import de.siphalor.tweed5.attributesextension.impl.AttributesExtensionImpl;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static de.siphalor.tweed5.attributesextension.api.AttributesExtension.attribute;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.stringReaderWriter;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.map;
|
||||
|
||||
public class AttributesReadWriteFilterExtensionImplWithPatchInfoTest {
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testOrder() {
|
||||
ConfigContainer<Map<String, Object>> configContainer = new DefaultConfigContainer<>();
|
||||
configContainer.registerExtension(AttributesReadWriteFilterExtensionImpl.class);
|
||||
configContainer.registerExtension(ReadWriteExtension.class);
|
||||
configContainer.registerExtension(AttributesExtensionImpl.class);
|
||||
configContainer.registerExtension(PatchExtension.class);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
var firstEntry = new SimpleConfigEntryImpl<>(configContainer, String.class)
|
||||
.apply(entryReaderWriter(stringReaderWriter()))
|
||||
.apply(attribute("type", "a"));
|
||||
var secondEntry = new SimpleConfigEntryImpl<>(configContainer, String.class)
|
||||
.apply(entryReaderWriter(stringReaderWriter()));
|
||||
var rootEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
(Class<Map<String, Object>>) (Class<?>) Map.class,
|
||||
HashMap::new,
|
||||
sequencedMap(List.of(entry("first", firstEntry), entry("second", secondEntry)))
|
||||
)
|
||||
.apply(entryReaderWriter(compoundReaderWriter()));
|
||||
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
var readWriteFilterExtension = configContainer.extension(AttributesReadWriteFilterExtension.class).orElseThrow();
|
||||
readWriteFilterExtension.markAttributeForFiltering("type");
|
||||
|
||||
configContainer.initialize();
|
||||
|
||||
var readWriteExtension = configContainer.extension(ReadWriteExtension.class).orElseThrow();
|
||||
var patchExtension = configContainer.extension(PatchExtension.class).orElseThrow();
|
||||
var filterExtension = configContainer.extension(AttributesReadWriteFilterExtension.class).orElseThrow();
|
||||
|
||||
var readExtData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||
var patchInfo = patchExtension.collectPatchInfo(readExtData);
|
||||
filterExtension.addFilter(readExtData, "type", "a");
|
||||
|
||||
var readResult = readWriteExtension.read(
|
||||
new HjsonReader(new HjsonLexer(new StringReader("""
|
||||
{
|
||||
"first": "FIRST",
|
||||
"second": "SECOND"
|
||||
}
|
||||
"""))),
|
||||
configContainer.rootEntry(),
|
||||
readExtData
|
||||
);
|
||||
|
||||
assertThat(readResult)
|
||||
.extracting(TweedReadResult::value)
|
||||
.asInstanceOf(map(String.class, Object.class))
|
||||
.isEqualTo(Map.of("first", "FIRST"));
|
||||
assertThat(patchInfo.containsEntry(firstEntry)).isTrue();
|
||||
assertThat(patchInfo.containsEntry(secondEntry)).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package de.siphalor.tweed5.commentloaderextension.api;
|
||||
|
||||
import de.siphalor.tweed5.commentloaderextension.impl.CommentLoaderExtensionImpl;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
|
||||
public interface CommentLoaderExtension extends TweedExtension {
|
||||
Class<? extends CommentLoaderExtension> DEFAULT = CommentLoaderExtensionImpl.class;
|
||||
|
||||
@@ -6,11 +6,12 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.dataapi.api.IntuitiveVisitingTweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducerMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde_api.api.IntuitiveVisitingTweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
@@ -44,8 +45,8 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
|
||||
}
|
||||
|
||||
@Override
|
||||
public Middleware<CommentProducer> commentMiddleware() {
|
||||
return new Middleware<CommentProducer>() {
|
||||
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
|
||||
return new Middleware<CommentProducer, CommentProducerMiddlewareContext>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -62,7 +63,10 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommentProducer process(CommentProducer inner) {
|
||||
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
|
||||
if (context.entry().extensionsData().get(loadedCommentAccess) == null) {
|
||||
return inner;
|
||||
}
|
||||
return entry -> {
|
||||
String loadedComment = entry.extensionsData().get(loadedCommentAccess);
|
||||
String innerComment = inner.createComment(entry);
|
||||
|
||||
@@ -7,7 +7,7 @@ import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.data.gson.GsonReader;
|
||||
import de.siphalor.tweed5.serde.gson.GsonReader;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface AddressableStructuredConfigEntry<T> extends StructuredConfigEntry<T> {
|
||||
@Override
|
||||
default AddressableStructuredConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
Object get(T value, String dataKey);
|
||||
}
|
||||
@@ -37,10 +37,10 @@ public interface CollectionConfigEntry<E, T extends Collection<E>> extends Struc
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
int index = 0;
|
||||
for (E item : value) {
|
||||
String indexString = Integer.toString(index);
|
||||
if (visitor.enterStructuredSubEntry("element", indexString)) {
|
||||
SubEntryKey subEntryKey = SubEntryKey.structured("element", Integer.toString(index));
|
||||
if (visitor.enterSubEntry(subEntryKey)) {
|
||||
elementEntry().visitInOrder(visitor, item);
|
||||
visitor.leaveStructuredSubEntry("element", indexString);
|
||||
visitor.leaveSubEntry(subEntryKey);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,13 @@ import de.siphalor.tweed5.core.api.Arity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface CompoundConfigEntry<T> extends StructuredConfigEntry<T> {
|
||||
public interface CompoundConfigEntry<T> extends MutableStructuredConfigEntry<T> {
|
||||
@Override
|
||||
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
MutableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
<V> void set(T compoundValue, String key, V value);
|
||||
<V> V get(T compoundValue, String key);
|
||||
|
||||
T instantiateCompoundValue();
|
||||
|
||||
@Override
|
||||
default void visitInOrder(ConfigEntryVisitor visitor) {
|
||||
if (visitor.enterStructuredEntry(this)) {
|
||||
|
||||
@@ -3,18 +3,18 @@ package de.siphalor.tweed5.core.api.entry;
|
||||
public interface ConfigEntryValueVisitor {
|
||||
<T> void visitEntry(ConfigEntry<T> entry, T value);
|
||||
|
||||
default <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
default <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||
default boolean enterSubEntry(SubEntryKey subEntryKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
|
||||
default void leaveSubEntry(SubEntryKey subEntryKey) {
|
||||
}
|
||||
|
||||
default <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
default <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface MutableStructuredConfigEntry<T> extends AddressableStructuredConfigEntry<T> {
|
||||
@Override
|
||||
default AddressableStructuredConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
AddressableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull T instantiateValue();
|
||||
void set(T value, String dataKey, Object subValue);
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface NullableConfigEntry<T extends @Nullable Object> extends StructuredConfigEntry<T> {
|
||||
public interface NullableConfigEntry<T extends @Nullable Object> extends AddressableStructuredConfigEntry<T> {
|
||||
String NON_NULL_KEY = ":nonNull";
|
||||
|
||||
@Override
|
||||
default NullableConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
AddressableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class SubEntryKey {
|
||||
String entry;
|
||||
@Nullable String value;
|
||||
@Nullable String data;
|
||||
|
||||
/**
|
||||
* Models the key of a transparent entry. Transparent entries are sub entries that only exist in the entry tree
|
||||
* but are not actually present in the data.
|
||||
* @param entryKey the key of the entry in the entry tree
|
||||
* @apiNote The resulting key is not addressable.
|
||||
*/
|
||||
public static SubEntryKey transparent(String entryKey) {
|
||||
return new SubEntryKey(entryKey, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Models the key of a transparent entry with a data key for use with {@link AddressableStructuredConfigEntry}s.
|
||||
* @param entryKey the key of the entry in the entry tree
|
||||
* @param dataKey the "address" of the data in the {@link AddressableStructuredConfigEntry}
|
||||
*/
|
||||
public static SubEntryKey transparentAddressable(String entryKey, String dataKey) {
|
||||
return new SubEntryKey(entryKey, null, dataKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* A "normal" sub entry key.
|
||||
* @param entryKey the key of the entry in the entry tree
|
||||
* @param valueKey a potentially differing key for the user-facing data tree
|
||||
*/
|
||||
public static SubEntryKey structured(String entryKey, String valueKey) {
|
||||
return new SubEntryKey(entryKey, valueKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* A sub entry key for {@link AddressableStructuredConfigEntry}s.
|
||||
* @param entryKey the key of the entry in the entry tree
|
||||
* @param valueKey a potentially differing key for the user-facing data tree
|
||||
* @param dataKey the "address" of the data in the {@link AddressableStructuredConfigEntry}
|
||||
* @apiNote {@code valueKey} and {@code dataKey} are usually a 1:1 mapping.
|
||||
*/
|
||||
public static SubEntryKey addressable(String entryKey, String valueKey, String dataKey) {
|
||||
return new SubEntryKey(entryKey, valueKey, dataKey);
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,11 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M, C> {
|
||||
private static final String CONTAINER_ID = "";
|
||||
|
||||
@Getter
|
||||
private List<Middleware<M>> middlewares = new ArrayList<>();
|
||||
private List<Middleware<M, C>> middlewares = new ArrayList<>();
|
||||
private final Set<String> middlewareIds = new HashSet<>();
|
||||
private boolean sealed = false;
|
||||
|
||||
@@ -22,10 +22,10 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAll(Collection<Middleware<M>> middlewares) {
|
||||
public void registerAll(Collection<Middleware<M, C>> middlewares) {
|
||||
requireUnsealed();
|
||||
|
||||
for (Middleware<M> middleware : middlewares) {
|
||||
for (Middleware<M, C> middleware : middlewares) {
|
||||
if (middleware.id().isEmpty()) {
|
||||
throw new IllegalArgumentException("Middleware id cannot be empty");
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Middleware<M> middleware) {
|
||||
public void register(Middleware<M, C> middleware) {
|
||||
requireUnsealed();
|
||||
|
||||
if (middleware.id().isEmpty()) {
|
||||
@@ -76,7 +76,7 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
|
||||
AcyclicGraphSorter sorter = new AcyclicGraphSorter(allMentionedMiddlewareIds.length);
|
||||
|
||||
for (Middleware<M> middleware : middlewares) {
|
||||
for (Middleware<M, C> middleware : middlewares) {
|
||||
Integer currentIndex = indecesByMiddlewareId.get(middleware.id());
|
||||
|
||||
middleware.mustComeAfter().stream()
|
||||
@@ -87,7 +87,8 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
.forEach(afterIndex -> sorter.addEdge(currentIndex, afterIndex));
|
||||
}
|
||||
|
||||
Map<String, Middleware<M>> middlewaresById = middlewares.stream().collect(Collectors.toMap(Middleware::id, Function.identity()));
|
||||
Map<String, Middleware<M, C>> middlewaresById = middlewares.stream()
|
||||
.collect(Collectors.toMap(Middleware::id, Function.identity()));
|
||||
|
||||
try {
|
||||
int[] sortedIndeces = sorter.sort();
|
||||
@@ -107,13 +108,27 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public M process(M inner, C context) {
|
||||
if (!sealed) {
|
||||
throw new IllegalStateException("Middleware container has not been sealed");
|
||||
}
|
||||
M combined = inner;
|
||||
for (int i = middlewares.size() - 1; i >= 0; i--) {
|
||||
Middleware<M, C> middleware = middlewares.get(i);
|
||||
combined = middleware.process(combined, context);
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public M process(M inner) {
|
||||
if (!sealed) {
|
||||
throw new IllegalStateException("Middleware container has not been sealed");
|
||||
}
|
||||
M combined = inner;
|
||||
for (int i = middlewares.size() - 1; i >= 0; i--) {
|
||||
Middleware<M> middleware = middlewares.get(i);
|
||||
Middleware<M, C> middleware = middlewares.get(i);
|
||||
combined = middleware.process(combined);
|
||||
}
|
||||
return combined;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package de.siphalor.tweed5.core.api.middleware;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public interface Middleware<M> {
|
||||
public interface Middleware<M, C> {
|
||||
String DEFAULT_START = "$default.start";
|
||||
String DEFAULT_END = "$default.end";
|
||||
|
||||
@@ -16,5 +18,13 @@ public interface Middleware<M> {
|
||||
return Collections.singleton(DEFAULT_START);
|
||||
}
|
||||
|
||||
M process(M inner);
|
||||
default M process(M inner, C context) {
|
||||
return process(inner);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.OverrideOnly
|
||||
default M process(M inner) {
|
||||
return inner;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package de.siphalor.tweed5.core.api.middleware;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface MiddlewareContainer<M> extends Middleware<M> {
|
||||
default void registerAll(Collection<Middleware<M>> middlewares) {
|
||||
public interface MiddlewareContainer<M, C> extends Middleware<M, C> {
|
||||
default void registerAll(Collection<Middleware<M, C>> middlewares) {
|
||||
middlewares.forEach(this::register);
|
||||
}
|
||||
void register(Middleware<M> middleware);
|
||||
void register(Middleware<M, C> middleware);
|
||||
void seal();
|
||||
Collection<Middleware<M>> middlewares();
|
||||
Collection<Middleware<M, C>> middlewares();
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
|
||||
public class CollectionConfigEntryImpl<E, T extends @NonNull Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
|
||||
private final IntFunction<T> collectionConstructor;
|
||||
private final ConfigEntry<E> elementEntry;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.NullableConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.SubEntryKey;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -24,6 +25,11 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
|
||||
this.nonNullEntry = nonNullEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(T value, String dataKey) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ConfigEntry<?>> subEntries() {
|
||||
return Collections.singletonMap(NON_NULL_KEY, nonNullEntry);
|
||||
@@ -33,9 +39,10 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (value != null) {
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
if (visitor.enterStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY)) {
|
||||
SubEntryKey subEntryKey = SubEntryKey.transparentAddressable(NON_NULL_KEY, NON_NULL_KEY);
|
||||
if (visitor.enterSubEntry(subEntryKey)) {
|
||||
nonNullEntry.visitInOrder(visitor, value);
|
||||
visitor.leaveStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY);
|
||||
visitor.leaveSubEntry(subEntryKey);
|
||||
}
|
||||
visitor.leaveStructuredEntry(this, value);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
|
||||
public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
|
||||
private final IntFunction<T> mapConstructor;
|
||||
private final Map<String, ConfigEntry<?>> compoundEntries;
|
||||
|
||||
@@ -28,16 +29,15 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> void set(T compoundValue, String key, V value) {
|
||||
requireKey(key);
|
||||
compoundValue.put(key, value);
|
||||
public void set(T compoundValue, String dataKey, Object value) {
|
||||
requireKey(dataKey);
|
||||
compoundValue.put(dataKey, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(T compoundValue, String key) {
|
||||
requireKey(key);
|
||||
//noinspection unchecked
|
||||
return (V) compoundValue.get(key);
|
||||
public Object get(T compoundValue, String dataKey) {
|
||||
requireKey(dataKey);
|
||||
return compoundValue.get(dataKey);
|
||||
}
|
||||
|
||||
private void requireKey(String key) {
|
||||
@@ -47,29 +47,28 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public T instantiateCompoundValue() {
|
||||
public T instantiateValue() {
|
||||
return mapConstructor.apply(compoundEntries.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
if (value != null) {
|
||||
compoundEntries.forEach((key, entry) -> {
|
||||
if (visitor.enterStructuredSubEntry(key, key)) {
|
||||
//noinspection unchecked
|
||||
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
||||
visitor.leaveStructuredSubEntry(key, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
compoundEntries.forEach((key, entry) -> {
|
||||
SubEntryKey subEntryKey = SubEntryKey.addressable(key, key, key);
|
||||
if (visitor.enterSubEntry(subEntryKey)) {
|
||||
//noinspection unchecked
|
||||
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
||||
visitor.leaveSubEntry(subEntryKey);
|
||||
}
|
||||
});
|
||||
visitor.leaveStructuredEntry(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deepCopy(T value) {
|
||||
T copy = instantiateCompoundValue();
|
||||
T copy = instantiateValue();
|
||||
value.forEach((String key, Object element) -> {
|
||||
//noinspection unchecked
|
||||
ConfigEntry<Object> elementEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
||||
|
||||
@@ -3,5 +3,5 @@ package de.siphalor.tweed5.defaultextensions.comment.api;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
|
||||
public interface CommentModifyingExtension {
|
||||
Middleware<CommentProducer> commentMiddleware();
|
||||
Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.siphalor.tweed5.defaultextensions.comment.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Builder
|
||||
public class CommentProducerMiddlewareContext {
|
||||
ConfigEntry<?> entry;
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducerMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
@@ -20,7 +21,7 @@ public class CommentExtensionImpl implements ReadWriteRelatedExtension, CommentE
|
||||
private final ConfigContainer<?> configContainer;
|
||||
@Getter
|
||||
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
|
||||
private final DefaultMiddlewareContainer<CommentProducer> middlewareContainer;
|
||||
private final DefaultMiddlewareContainer<CommentProducer, CommentProducerMiddlewareContext> middlewareContainer;
|
||||
@Getter
|
||||
private @Nullable PatchworkPartAccess<Boolean> writerInstalledReadWriteContextAccess;
|
||||
|
||||
@@ -71,7 +72,10 @@ public class CommentExtensionImpl implements ReadWriteRelatedExtension, CommentE
|
||||
public void recomputeFullComments() {
|
||||
configContainer.rootEntry().visitInOrder(entry -> {
|
||||
CustomEntryData entryData = getOrCreateCustomEntryData(entry);
|
||||
entryData.commentProducer(middlewareContainer.process(_entry -> entryData.baseComment()));
|
||||
entryData.commentProducer(middlewareContainer.process(
|
||||
_entry -> entryData.baseComment(),
|
||||
CommentProducerMiddlewareContext.builder().entry(entry).build()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ package de.siphalor.tweed5.defaultextensions.comment.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.DelegatingTweedDataWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.WriterMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde_api.api.DelegatingTweedDataWriter;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataCommentDecoration;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
@@ -18,7 +18,7 @@ import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?, ?>> {
|
||||
class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext> {
|
||||
private final CommentExtensionImpl commentExtension;
|
||||
|
||||
@Override
|
||||
@@ -27,15 +27,15 @@ class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
|
||||
PatchworkPartAccess<Boolean> writerInstalledAccess = commentExtension.writerInstalledReadWriteContextAccess();
|
||||
assert writerInstalledAccess != null;
|
||||
|
||||
//noinspection unchecked
|
||||
TweedEntryWriter<Object, ConfigEntry<Object>> innerCasted = (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
return (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) (writer, value, entry, context) -> {
|
||||
if (!Boolean.TRUE.equals(context.extensionsData().get(writerInstalledAccess))) {
|
||||
context.extensionsData().set(writerInstalledAccess, Boolean.TRUE);
|
||||
return (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) (writer, value, entry, writeContext) -> {
|
||||
if (!Boolean.TRUE.equals(writeContext.extensionsData().get(writerInstalledAccess))) {
|
||||
writeContext.extensionsData().set(writerInstalledAccess, Boolean.TRUE);
|
||||
|
||||
writer = new MapEntryKeyDeferringWriter(writer);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?
|
||||
writer.visitDecoration(new PiercingCommentDecoration(() -> comment));
|
||||
}
|
||||
|
||||
innerCasted.write(writer, value, entry, context);
|
||||
innerCasted.write(writer, value, entry, writeContext);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,11 @@ package de.siphalor.tweed5.defaultextensions.patch.impl;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReadException;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReaderMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
@@ -62,7 +61,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
||||
if (targetValue != null) {
|
||||
targetCompoundValue = targetValue;
|
||||
} else {
|
||||
targetCompoundValue = compoundEntry.instantiateCompoundValue();
|
||||
targetCompoundValue = compoundEntry.instantiateValue();
|
||||
}
|
||||
compoundEntry.subEntries().forEach((key, subEntry) -> {
|
||||
if (!patchInfo.containsEntry(subEntry)) {
|
||||
@@ -70,7 +69,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
||||
}
|
||||
compoundEntry.set(
|
||||
targetCompoundValue, key, patch(
|
||||
subEntry,
|
||||
(ConfigEntry<Object>) subEntry,
|
||||
compoundEntry.get(targetCompoundValue, key),
|
||||
compoundEntry.get(patchValue, key),
|
||||
patchInfo
|
||||
@@ -83,33 +82,26 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
||||
}
|
||||
}
|
||||
|
||||
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
|
||||
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
|
||||
@Override
|
||||
public String id() {
|
||||
return "patch-info-collector";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
|
||||
assert readWriteContextDataAccess != null;
|
||||
|
||||
//noinspection unchecked
|
||||
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted =
|
||||
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
return new TweedEntryReader<@Nullable Object, ConfigEntry<Object>>() {
|
||||
@Override
|
||||
public @Nullable Object read(
|
||||
TweedDataReader reader,
|
||||
ConfigEntry<Object> entry,
|
||||
TweedReadContext context
|
||||
) throws TweedEntryReadException {
|
||||
Object readValue = innerCasted.read(reader, entry, context);
|
||||
ReadWriteContextCustomData customData = context.extensionsData().get(readWriteContextDataAccess);
|
||||
if (customData != null && customData.patchInfo() != null) {
|
||||
customData.patchInfo().addEntry(entry);
|
||||
}
|
||||
return readValue;
|
||||
return (TweedEntryReader<Object, ConfigEntry<Object>>) (reader, entry, readContext) -> {
|
||||
TweedReadResult<Object> readResult = innerCasted.read(reader, entry, readContext);
|
||||
ReadWriteContextCustomData customData = readContext.extensionsData().get(readWriteContextDataAccess);
|
||||
if (customData != null && customData.patchInfo() != null) {
|
||||
customData.patchInfo().addEntry(entry);
|
||||
}
|
||||
return readResult;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,11 @@ public interface PathTracking {
|
||||
|
||||
void pushPathPart(String pathPart);
|
||||
|
||||
void pushEmptyPathPart();
|
||||
|
||||
void popPathPart();
|
||||
|
||||
String currentPath();
|
||||
|
||||
String[] currentPathParts();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.SubEntryKey;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -16,27 +18,31 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
return delegate.enterStructuredEntry(entry, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey);
|
||||
public boolean enterSubEntry(SubEntryKey subEntryKey) {
|
||||
boolean enter = delegate.enterSubEntry(subEntryKey);
|
||||
if (enter) {
|
||||
pathTracking.pushPathPart(entryKey, valueKey);
|
||||
if (subEntryKey.value() != null) {
|
||||
pathTracking.pushPathPart(subEntryKey.entry(), subEntryKey.value());
|
||||
} else {
|
||||
pathTracking.pushEmptyValuePathPart(subEntryKey.entry());
|
||||
}
|
||||
}
|
||||
return enter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leaveStructuredSubEntry(String entryKey, String valueKey) {
|
||||
delegate.leaveStructuredSubEntry(entryKey, valueKey);
|
||||
public void leaveSubEntry(SubEntryKey subEntryKey) {
|
||||
delegate.leaveSubEntry(subEntryKey);
|
||||
pathTracking.popPathPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
delegate.leaveStructuredEntry(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataUnsupportedValueException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataUnsupportedValueException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.impl.PatherExtensionImpl;
|
||||
|
||||
public interface PatherExtension extends TweedExtension {
|
||||
|
||||
@@ -17,6 +17,17 @@ public final class ValuePathTracking implements PathTracking {
|
||||
valuePathTracking.pushPathPart(valuePathPart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushEmptyPathPart() {
|
||||
entryPathTracking.pushEmptyPathPart();
|
||||
valuePathTracking.pushEmptyPathPart();
|
||||
}
|
||||
|
||||
public void pushEmptyValuePathPart(String entryPathPart) {
|
||||
entryPathTracking.pushPathPart(entryPathPart);
|
||||
valuePathTracking.pushEmptyPathPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popPathPart() {
|
||||
valuePathTracking.popPathPart();
|
||||
@@ -31,4 +42,13 @@ public final class ValuePathTracking implements PathTracking {
|
||||
public String currentEntryPath() {
|
||||
return entryPathTracking.currentPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] currentPathParts() {
|
||||
return valuePathTracking.currentPathParts();
|
||||
}
|
||||
|
||||
public String[] currentEntryPathParts() {
|
||||
return entryPathTracking.currentPathParts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package de.siphalor.tweed5.defaultextensions.pather.impl;
|
||||
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class PathTrackingImpl implements PathTracking {
|
||||
private final StringBuilder pathBuilder = new StringBuilder(256);
|
||||
private final Deque<String> pathParts = new ArrayDeque<>(50);
|
||||
private final Deque<@Nullable String> pathParts = new ArrayDeque<>(50);
|
||||
|
||||
@Override
|
||||
public void pushPathPart(String entryPathPart) {
|
||||
@@ -15,11 +16,18 @@ public class PathTrackingImpl implements PathTracking {
|
||||
pathBuilder.append(".").append(entryPathPart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushEmptyPathPart() {
|
||||
pathParts.push(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popPathPart() {
|
||||
if (!pathParts.isEmpty()) {
|
||||
String poppedPart = pathParts.pop();
|
||||
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
|
||||
if (poppedPart != null) {
|
||||
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +35,9 @@ public class PathTrackingImpl implements PathTracking {
|
||||
public String currentPath() {
|
||||
return pathBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] currentPathParts() {
|
||||
return pathParts.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package de.siphalor.tweed5.defaultextensions.pather.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde.extension.api.*;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReaderMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.WriterMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataReader;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataVisitor;
|
||||
@@ -48,75 +50,62 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
||||
return pathTracking.currentPath();
|
||||
}
|
||||
|
||||
private Middleware<TweedEntryReader<?, ?>> createEntryReaderMiddleware() {
|
||||
return new Middleware<TweedEntryReader<?, ?>>() {
|
||||
private Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> createEntryReaderMiddleware() {
|
||||
return new Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
|
||||
assert rwContextPathTrackingAccess != null;
|
||||
|
||||
//noinspection unchecked
|
||||
val castedInner = (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
|
||||
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
|
||||
PathTracking pathTracking = context.extensionsData().get(rwContextPathTrackingAccess);
|
||||
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext readContext) -> {
|
||||
PathTracking pathTracking = readContext.extensionsData().get(rwContextPathTrackingAccess);
|
||||
if (pathTracking != null) {
|
||||
return castedInner.read(reader, entry, context);
|
||||
return castedInner.read(reader, entry, readContext);
|
||||
}
|
||||
|
||||
pathTracking = PathTracking.create();
|
||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||
try {
|
||||
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context);
|
||||
} catch (TweedEntryReadException e) {
|
||||
val exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||
if (exceptionPathTracking != null) {
|
||||
throw new TweedEntryReadException(
|
||||
"Exception while reading entry at "
|
||||
+ exceptionPathTracking.currentPath()
|
||||
+ ": " + e.getMessage(),
|
||||
e
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
readContext.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, readContext);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Middleware<TweedEntryWriter<?, ?>> createEntryWriterMiddleware() {
|
||||
return new Middleware<TweedEntryWriter<?, ?>>() {
|
||||
private Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext> createEntryWriterMiddleware() {
|
||||
return new Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
|
||||
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
|
||||
assert rwContextPathTrackingAccess != null;
|
||||
|
||||
//noinspection unchecked
|
||||
val castedInner = (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
|
||||
return (TweedDataVisitor writer, Object value, ConfigEntry<Object> entry, TweedWriteContext context) -> {
|
||||
PathTracking pathTracking = context.extensionsData().get(rwContextPathTrackingAccess);
|
||||
return (TweedDataVisitor writer, Object value, ConfigEntry<Object> entry, TweedWriteContext writeContext) -> {
|
||||
PathTracking pathTracking = writeContext.extensionsData().get(rwContextPathTrackingAccess);
|
||||
if (pathTracking != null) {
|
||||
castedInner.write(writer, value, entry, context);
|
||||
castedInner.write(writer, value, entry, writeContext);
|
||||
return;
|
||||
}
|
||||
|
||||
pathTracking = PathTracking.create();
|
||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||
writeContext.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||
try {
|
||||
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
||||
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, writeContext);
|
||||
} catch (TweedEntryWriteException e) {
|
||||
val exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||
PathTracking exceptionPathTracking =
|
||||
e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||
if (exceptionPathTracking != null) {
|
||||
throw new TweedEntryWriteException(
|
||||
"Exception while writing entry at "
|
||||
|
||||
@@ -3,14 +3,15 @@ package de.siphalor.tweed5.defaultextensions.readfallback.impl;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReadException;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReaderMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
@@ -30,9 +31,7 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
|
||||
PresetsExtension presetsExtension = configContainer.extension(PresetsExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException(getClass().getSimpleName()
|
||||
+ " requires " + ReadFallbackExtension.class.getSimpleName()));
|
||||
PatherExtension patherExtension = configContainer.extension(PatherExtension.class).orElse(null);
|
||||
|
||||
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>>() {
|
||||
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -49,26 +48,19 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
|
||||
//noinspection unchecked
|
||||
TweedEntryReader<Object, ConfigEntry<Object>> castedInner =
|
||||
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||
return (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) (reader, entry, context) -> {
|
||||
try {
|
||||
return castedInner.read(reader, entry, context);
|
||||
} catch (TweedEntryReadException e) {
|
||||
if (patherExtension == null) {
|
||||
log.error("Failed to read entry: " + e.getMessage(), e);
|
||||
} else {
|
||||
log.error(
|
||||
"Failed to read entry: " + e.getMessage()
|
||||
+ " at " + patherExtension.getPath(context),
|
||||
e
|
||||
);
|
||||
}
|
||||
return presetsExtension.presetValue(entry, PresetsExtension.DEFAULT_PRESET_NAME);
|
||||
}
|
||||
};
|
||||
return (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) (reader, entry, readContext) ->
|
||||
castedInner.read(reader, entry, readContext).catchError(
|
||||
issues -> {
|
||||
Object fallback =
|
||||
presetsExtension.presetValue(entry, PresetsExtension.DEFAULT_PRESET_NAME);
|
||||
return TweedReadResult.withIssues(fallback, issues);
|
||||
},
|
||||
readContext
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResu
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.validators.SimpleValidatorMiddleware;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.impl.ValidationExtensionImpl;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@@ -55,7 +56,10 @@ public interface ValidationExtension extends TweedExtension {
|
||||
lastId = id;
|
||||
}
|
||||
}
|
||||
<T> void addValidatorMiddleware(ConfigEntry<T> entry, Middleware<ConfigEntryValidator> validator);
|
||||
<T> void addValidatorMiddleware(
|
||||
ConfigEntry<T> entry,
|
||||
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator
|
||||
);
|
||||
|
||||
ValidationIssues captureValidationIssues(Patchwork readContextExtensionsData);
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@ package de.siphalor.tweed5.defaultextensions.validation.api;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
|
||||
public interface ValidationProvidingExtension {
|
||||
Middleware<ConfigEntryValidator> validationMiddleware();
|
||||
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validationMiddleware();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.siphalor.tweed5.defaultextensions.validation.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor(access = lombok.AccessLevel.PRIVATE)
|
||||
@Builder
|
||||
public class ValidatorMiddlewareContext {
|
||||
ConfigEntry<?> entry;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package de.siphalor.tweed5.defaultextensions.validation.api.validators;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidatorMiddlewareContext;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -10,12 +11,12 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SimpleValidatorMiddleware implements Middleware<ConfigEntryValidator> {
|
||||
public class SimpleValidatorMiddleware implements Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> {
|
||||
String id;
|
||||
ConfigEntryValidator validator;
|
||||
|
||||
@Override
|
||||
public ConfigEntryValidator process(ConfigEntryValidator inner) {
|
||||
public ConfigEntryValidator process(ConfigEntryValidator inner, ValidatorMiddlewareContext context) {
|
||||
return new ConfigEntryValidator() {
|
||||
@Override
|
||||
public <T extends @Nullable Object> ValidationResult<T> validate(ConfigEntry<T> configEntry, T value) {
|
||||
|
||||
@@ -3,19 +3,15 @@ package de.siphalor.tweed5.defaultextensions.validation.impl;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.core.api.middleware.MiddlewareContainer;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReadException;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducerMiddlewareContext;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
||||
@@ -23,13 +19,23 @@ import de.siphalor.tweed5.defaultextensions.pather.api.ValuePathTracking;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidatorMiddlewareContext;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssue;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssues;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.extension.ReaderMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadIssue;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import lombok.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@@ -68,8 +74,8 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
|
||||
private final ConfigContainer<?> configContainer;
|
||||
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
|
||||
private final MiddlewareContainer<ConfigEntryValidator> entryValidatorMiddlewareContainer
|
||||
= new DefaultMiddlewareContainer<>();
|
||||
private final MiddlewareContainer<ConfigEntryValidator, ValidatorMiddlewareContext>
|
||||
entryValidatorMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
private @Nullable PatchworkPartAccess<ValidationIssues> readContextValidationIssuesAccess;
|
||||
private @Nullable PatherExtension patherExtension;
|
||||
|
||||
@@ -95,15 +101,15 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public Middleware<CommentProducer> commentMiddleware() {
|
||||
return new Middleware<CommentProducer>() {
|
||||
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
|
||||
return new Middleware<CommentProducer, CommentProducerMiddlewareContext>() {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommentProducer process(CommentProducer inner) {
|
||||
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
|
||||
return entry -> {
|
||||
String baseComment = inner.createComment(entry);
|
||||
CustomEntryData entryData = entry.extensionsData().get(customEntryDataAccess);
|
||||
@@ -133,7 +139,10 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addValidatorMiddleware(ConfigEntry<T> entry, Middleware<ConfigEntryValidator> validator) {
|
||||
public <T> void addValidatorMiddleware(
|
||||
ConfigEntry<T> entry,
|
||||
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator
|
||||
) {
|
||||
CustomEntryData entryData = getOrCreateCustomEntryData(entry);
|
||||
entryData.addValidator(validator);
|
||||
}
|
||||
@@ -150,13 +159,20 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
CustomEntryData entryData = getOrCreateCustomEntryData(configEntry);
|
||||
|
||||
if (entryData.validators().isEmpty()) {
|
||||
entryData.completeValidator(entryValidatorMiddlewareContainer.process(baseValidator));
|
||||
entryData.completeValidator(entryValidatorMiddlewareContainer.process(
|
||||
baseValidator,
|
||||
ValidatorMiddlewareContext.builder().entry(configEntry).build()
|
||||
));
|
||||
} else {
|
||||
DefaultMiddlewareContainer<ConfigEntryValidator> entrySpecificValidatorContainer = new DefaultMiddlewareContainer<>();
|
||||
DefaultMiddlewareContainer<ConfigEntryValidator, ValidatorMiddlewareContext> entrySpecificValidatorContainer
|
||||
= new DefaultMiddlewareContainer<>();
|
||||
entrySpecificValidatorContainer.registerAll(entryValidatorMiddlewareContainer.middlewares());
|
||||
entrySpecificValidatorContainer.registerAll(entryData.validators());
|
||||
entrySpecificValidatorContainer.seal();
|
||||
entryData.completeValidator(entrySpecificValidatorContainer.process(baseValidator));
|
||||
entryData.completeValidator(entrySpecificValidatorContainer.process(
|
||||
baseValidator,
|
||||
ValidatorMiddlewareContext.builder().entry(configEntry).build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +213,14 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
@Data
|
||||
private static class CustomEntryData {
|
||||
@Setter(AccessLevel.NONE)
|
||||
private @Nullable List<Middleware<ConfigEntryValidator>> validators;
|
||||
private @Nullable List<Middleware<ConfigEntryValidator, ValidatorMiddlewareContext>> validators;
|
||||
private @Nullable ConfigEntryValidator completeValidator;
|
||||
|
||||
public List<Middleware<ConfigEntryValidator>> validators() {
|
||||
public List<Middleware<ConfigEntryValidator, ValidatorMiddlewareContext>> validators() {
|
||||
return validators == null ? Collections.emptyList() : validators;
|
||||
}
|
||||
|
||||
public void addValidator(Middleware<ConfigEntryValidator> validator) {
|
||||
public void addValidator(Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator) {
|
||||
if (validators == null) {
|
||||
validators = new ArrayList<>();
|
||||
}
|
||||
@@ -212,7 +228,8 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryValidationReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
|
||||
private class EntryValidationReaderMiddleware
|
||||
implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -224,43 +241,55 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
|
||||
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
|
||||
assert readContextValidationIssuesAccess != null && patherExtension != null;
|
||||
|
||||
//noinspection unchecked
|
||||
TweedEntryReader<Object, ConfigEntry<Object>> castedInner = (TweedEntryReader<Object, ConfigEntry<Object>>) inner;
|
||||
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
|
||||
ValidationIssues validationIssues = getOrCreateValidationIssues(context.extensionsData());
|
||||
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext readContext) -> {
|
||||
ValidationIssues validationIssues = getOrCreateValidationIssues(readContext.extensionsData());
|
||||
|
||||
Object value = castedInner.read(reader, entry, context);
|
||||
return castedInner.read(reader, entry, readContext).andThen(value -> {
|
||||
ConfigEntryValidator entryValidator = entry.extensionsData()
|
||||
.get(customEntryDataAccess)
|
||||
.completeValidator();
|
||||
assert entryValidator != null;
|
||||
|
||||
ConfigEntryValidator entryValidator = entry.extensionsData()
|
||||
.get(customEntryDataAccess)
|
||||
.completeValidator();
|
||||
assert entryValidator != null;
|
||||
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
|
||||
|
||||
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
|
||||
if (validationResult.issues().isEmpty()) {
|
||||
return TweedReadResult.ok(validationResult.value());
|
||||
}
|
||||
|
||||
if (!validationResult.issues().isEmpty()) {
|
||||
String path = patherExtension.getPath(context);
|
||||
String path = patherExtension.getPath(readContext);
|
||||
validationIssues.issuesByPath().put(path, new ValidationIssues.EntryIssues(
|
||||
entry,
|
||||
validationResult.issues()
|
||||
));
|
||||
}
|
||||
|
||||
if (validationResult.hasError()) {
|
||||
throw new TweedEntryReadException(
|
||||
"Failed to validate entry: " + validationResult.issues(),
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
return validationResult.value();
|
||||
if (validationResult.hasError()) {
|
||||
return TweedReadResult.failed(
|
||||
mapValidationIssuesToReadIssues(validationResult.issues(), readContext)
|
||||
);
|
||||
} else {
|
||||
return TweedReadResult.withIssues(
|
||||
validationResult.value(),
|
||||
mapValidationIssuesToReadIssues(validationResult.issues(), readContext)
|
||||
);
|
||||
}
|
||||
}, readContext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static TweedReadIssue[] mapValidationIssuesToReadIssues(
|
||||
Collection<ValidationIssue> issues,
|
||||
TweedReadContext context
|
||||
) {
|
||||
return issues.stream().map(
|
||||
validationIssue -> TweedReadIssue.error(validationIssue.message(), context)
|
||||
).toArray(TweedReadIssue[]::new);
|
||||
}
|
||||
|
||||
private ValidationIssues getOrCreateValidationIssues(Patchwork readContextExtensionsData) {
|
||||
assert readContextValidationIssuesAccess != null;
|
||||
ValidationIssues validationIssues = readContextExtensionsData.get(readContextValidationIssuesAccess);
|
||||
@@ -290,12 +319,12 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidatorMiddlewareContext;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssue;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||
@@ -62,11 +63,11 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
}
|
||||
|
||||
@Override
|
||||
public Middleware<ConfigEntryValidator> validationMiddleware() {
|
||||
public Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validationMiddleware() {
|
||||
return new ValidationFallbackMiddleware();
|
||||
}
|
||||
|
||||
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator> {
|
||||
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> {
|
||||
@Override
|
||||
public String id() {
|
||||
return EXTENSION_ID;
|
||||
@@ -83,7 +84,7 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigEntryValidator process(ConfigEntryValidator inner) {
|
||||
public ConfigEntryValidator process(ConfigEntryValidator inner, ValidatorMiddlewareContext context) {
|
||||
return new ConfigEntryValidator() {
|
||||
@Override
|
||||
public <T extends @Nullable Object> ValidationResult<T> validate(ConfigEntry<T> configEntry, T value) {
|
||||
|
||||
@@ -9,9 +9,10 @@ import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducerMiddlewareContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||
@@ -26,9 +27,9 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension.baseComment;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -144,7 +145,7 @@ class CommentExtensionImplTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Middleware<CommentProducer> commentMiddleware() {
|
||||
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
|
||||
return new Middleware<>() {
|
||||
@Override
|
||||
public String id() {
|
||||
@@ -152,7 +153,7 @@ class CommentExtensionImplTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommentProducer process(CommentProducer inner) {
|
||||
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
|
||||
return entry -> "The comment is:\n" + inner.createComment(entry) + "\nEND";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,9 +8,10 @@ import de.siphalor.tweed5.core.impl.entry.CollectionConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -26,9 +27,9 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.read;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.read;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -150,7 +151,7 @@ class PatchExtensionImplTest {
|
||||
PatchExtension patchExtension = configContainer.extension(PatchExtension.class).orElseThrow();
|
||||
|
||||
var patchInfo = new AtomicReference<@Nullable PatchInfo>();
|
||||
Map<String, Object> patchValue = rootEntry.call(read(
|
||||
TweedReadResult<Map<String, Object>> patchResult = rootEntry.call(read(
|
||||
reader, extensionsData ->
|
||||
patchInfo.set(patchExtension.collectPatchInfo(extensionsData))
|
||||
));
|
||||
@@ -158,7 +159,7 @@ class PatchExtensionImplTest {
|
||||
Map<String, Object> resultValue = patchExtension.patch(
|
||||
rootEntry,
|
||||
baseValue,
|
||||
patchValue,
|
||||
patchResult.value(),
|
||||
Objects.requireNonNull(patchInfo.get())
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataTokens;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataTokens;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
package de.siphalor.tweed5.defaultextensions.readfallback.impl;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReadException;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadIssue;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.testutils.generic.log.LogCaptureMockitoExtension;
|
||||
import de.siphalor.tweed5.testutils.generic.log.LogsCaptor;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -34,12 +34,11 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.read;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||
import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.presetValue;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.read;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
|
||||
|
||||
@ExtendWith(LogCaptureMockitoExtension.class)
|
||||
@NullMarked
|
||||
@@ -59,12 +58,18 @@ class ReadFallbackExtensionImplTest {
|
||||
configContainer.attachTree(entry);
|
||||
configContainer.initialize();
|
||||
|
||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12")))))).isEqualTo(12);
|
||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12"))))))
|
||||
.isEqualTo(TweedReadResult.ok(12));
|
||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
|
||||
logsCaptor.clear();
|
||||
|
||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).isEqualTo(-1);
|
||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).hasSize(1);
|
||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).satisfies(
|
||||
r -> assertThat(r).extracting(TweedReadResult::value).isEqualTo(-1),
|
||||
r -> assertThat(r.issues()).singleElement()
|
||||
.extracting(TweedReadIssue::exception)
|
||||
.extracting(Throwable::getMessage)
|
||||
.isEqualTo("Value is not even")
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@@ -105,6 +110,7 @@ class ReadFallbackExtensionImplTest {
|
||||
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
||||
"{first: {second: 12}}"
|
||||
))))))
|
||||
.extracting(TweedReadResult::value)
|
||||
.extracting(map -> (Map<String, Object>) map.get("first"))
|
||||
.extracting(map -> (Integer) map.get("second"))
|
||||
.isEqualTo(12);
|
||||
@@ -114,22 +120,20 @@ class ReadFallbackExtensionImplTest {
|
||||
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
||||
"{first: {second: 13}}"
|
||||
))))))
|
||||
.extracting(TweedReadResult::value)
|
||||
.extracting(map -> (Map<String, Object>) map.get("first"))
|
||||
.extracting(map -> (Integer) map.get("second"))
|
||||
.isEqualTo(-1);
|
||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).singleElement()
|
||||
.extracting(ILoggingEvent::getMessage)
|
||||
.asInstanceOf(STRING)
|
||||
.contains("first.second");
|
||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
|
||||
}
|
||||
|
||||
private static class EvenIntReader implements TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> {
|
||||
@Override
|
||||
public Integer read(
|
||||
public TweedReadResult<Integer> read(
|
||||
TweedDataReader reader,
|
||||
ConfigEntry<Integer> entry,
|
||||
TweedReadContext context
|
||||
) throws TweedEntryReadException {
|
||||
) {
|
||||
int value;
|
||||
try {
|
||||
value = reader.readToken().readAsInt();
|
||||
@@ -137,9 +141,9 @@ class ReadFallbackExtensionImplTest {
|
||||
throw new IllegalStateException("Should not be called", e);
|
||||
}
|
||||
if (value % 2 == 0) {
|
||||
return value;
|
||||
return TweedReadResult.ok(value);
|
||||
} else {
|
||||
throw new TweedEntryReadException("Value is not even", context);
|
||||
return TweedReadResult.error(TweedReadIssue.error("Value is not even", context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||
@@ -31,10 +32,10 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension.baseComment;
|
||||
import static de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension.validators;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -156,12 +157,14 @@ class ValidationExtensionImplTest {
|
||||
}
|
||||
""")));
|
||||
var validationIssues = new AtomicReference<@Nullable ValidationIssues>();
|
||||
Map<String, Object> value = configContainer.rootEntry().call(read(
|
||||
TweedReadResult<Map<String, Object>> value = configContainer.rootEntry().call(read(
|
||||
reader,
|
||||
extensionsData -> validationIssues.set(validationExtension.captureValidationIssues(extensionsData))
|
||||
));
|
||||
|
||||
assertThat(value).isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
|
||||
assertThat(value)
|
||||
.extracting(TweedReadResult::value)
|
||||
.isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
|
||||
//noinspection DataFlowIssue
|
||||
assertThat(validationIssues.get()).isNotNull().satisfies(
|
||||
vi -> assertValidationIssue(
|
||||
|
||||
@@ -4,11 +4,12 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonCommentType;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||
@@ -26,10 +27,11 @@ import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.presetValue;
|
||||
import static de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension.validators;
|
||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ValidationFallbackExtensionImplTest {
|
||||
@@ -114,8 +116,9 @@ class ValidationFallbackExtensionImplTest {
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"0", "7", "123", "null"})
|
||||
void fallbackTriggers(String input) {
|
||||
Integer result = intEntry.call(read(new HjsonReader(new HjsonLexer(new StringReader(input)))));
|
||||
assertEquals(3, result);
|
||||
TweedReadResult<Integer> result = intEntry.call(read(new HjsonReader(new HjsonLexer(new StringReader(input)))));
|
||||
assertThat(result).extracting(TweedReadResult::value).isEqualTo(3);
|
||||
assertThat(result.issues()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
2
tweed5/gradle.properties
Normal file
2
tweed5/gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
org.gradle.jvmargs = -Xmx2G
|
||||
org.gradle.configuration-cache = true
|
||||
2
tweed5/lombok.config
Normal file
2
tweed5/lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
lombok.accessors.fluent = true
|
||||
lombok.addLombokGeneratedAnnotation = true
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user