Compare commits

3 Commits
main ... v0.7

183 changed files with 941 additions and 2602 deletions

1
.gitattributes vendored
View File

@@ -1,4 +1,3 @@
.gitattributes text eol=lf
*.sh text eol=lf
gradlew text eol=lf
gradlew.bat text eol=crlf

5
.gitignore vendored
View File

@@ -5,11 +5,6 @@ build/
!**/src/test/**/build/
.kotlin/
### Gradle in Subprojects ###
*/gradlew
*/gradlew.bat
*/gradle/wrapper
### IntelliJ IDEA ###
.idea/
*.iws

View File

@@ -5,20 +5,15 @@ 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).
## [Unreleased]
## [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.
### Added
- `networking`: Added module for Minecraft networking.
- `coat-bridge`: Added experimental text mapper based on Tweed Serde.
## [0.7.1] - 2026-02-08
### Changed
- **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.
- Relicensed to MPL-2.0.
## [0.7.0] - 2025-12-19

View File

@@ -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_RUNTIME))
attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.JAVA_API))
project.afterEvaluate {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion.get().toInt())

View File

@@ -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,6 +13,12 @@ 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()
}
@@ -33,9 +39,40 @@ 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") {

View File

@@ -1,8 +1,5 @@
import java.util.Properties
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()) }
}
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()) }

View File

@@ -1,48 +0,0 @@
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)
}

View File

@@ -1,6 +1,6 @@
org.gradle.jvmargs = -Xmx2G
org.gradle.configuration-cache = true
tweed5.version = 0.7.1
tweed5.version = 0.7.2
git.url = https://gitea.siphalor.de/siphalor/tweed5

Binary file not shown.

5
gradlew vendored
View File

@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/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,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ 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" )
@@ -212,7 +210,6 @@ 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
View File

@@ -70,11 +70,10 @@ 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%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -7,8 +7,9 @@ plugins {
dependencies {
implementation("de.siphalor.tweed5:tweed5-conventions")
implementation("de.siphalor.tweed5:tweed5-conventions-helpers")
implementation(pluginMarker(mcCommonLibs.plugins.fabric.loom))
implementation(pluginMarker(mcCommonLibs.plugins.smcmtk))
implementation(pluginMarker(mcCommonLibs.plugins.jcyo))
implementation(pluginMarker(mcLibs.plugins.fabric.loom))
implementation(pluginMarker(libs.plugins.lombok))
implementation(pluginMarker(libs.plugins.shadow))
}

View File

@@ -1,13 +1,15 @@
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("fabric-loom")
id("de.siphalor.tweed5.unit-tests")
id("de.siphalor.minecraft-modding-toolkit.project-plugin")
id("de.siphalor.tweed5.publishing")
id("de.siphalor.tweed5.expanded-sources-jar")
id("de.siphalor.jcyo")
@@ -17,14 +19,21 @@ 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 {
@@ -39,7 +48,12 @@ val testmod by sourceSets.creating {
runtimeClasspath += sourceSets.main.get().runtimeClasspath
}
loom {
smcmtk {
useMojangMappings()
createModConfigurations(listOf(sourceSets.main.get(), testmod))
}
extensions.configure<LoomGradleExtensionAPI>() {
runs {
create("testmodClient") {
client()
@@ -47,7 +61,6 @@ loom {
source(testmod)
}
}
createRemapConfigurations(testmod)
}
// For some reason dependencyResolutionManagement from the settings.gradle doesn't seem to be passed through correctly,
@@ -79,12 +92,8 @@ configurations {
}
dependencies {
minecraft(mcCatalog.findLibrary("minecraft").get())
mappings(loom.layered {
officialMojangMappings()
parchment("org.parchmentmc.data:parchment-$minecraftVersion:${getMcCatalogVersion("parchment")}@zip")
})
modImplementation(mcCommonLibs.fabric.loader)
"minecraft"(mcCatalog.findLibrary("minecraft").get())
"modImplementation"(mcCommonLibs.fabric.loader)
"modTestmodImplementation"(mcCommonLibs.fabric.loader)
compileOnly(libs.jspecify.annotations)
@@ -92,6 +101,11 @@ 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] }
@@ -117,31 +131,6 @@ 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)
@@ -160,7 +149,8 @@ shadow {
addShadowVariantIntoJavaComponent = false
}
tasks.remapJar {
tasks.findByName("remapJar")?.apply {
this as RemapJarTask
dependsOn(tasks.shadowJar)
inputFile = tasks.shadowJar.get().archiveFile
}

View File

@@ -8,16 +8,17 @@ 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("fabric-key-binding-api-v1", "fabric-resource-loader-v0").forEach {
listOf(smcmtk.mcProps.getting("fabric.api.key_mapping").get(), "fabric-resource-loader-v0").forEach {
modTestmodImplementation(fabricApi.module(it, mcLibs.versions.fabric.api.get()))
}
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModApiElements"))
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModElements"))
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.api)
modTestmodImplementation(mcLibs.amecs.priorityKeyMappings)
testmodImplementation("de.siphalor.tweed5:tweed5-serde-hjson")
}

View File

@@ -5,7 +5,6 @@ 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;
@@ -67,10 +66,4 @@ 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});
}
}

View File

@@ -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.extern.apachecommons.CommonsLog;
import lombok.CustomLog;
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;
@CommonsLog
@CustomLog
public class BasicTweedCoatEntryHandler<T extends @Nullable Object> implements ConfigEntryHandler<T> {
protected final ConfigEntry<T> configEntry;
protected final T defaultValue;

View File

@@ -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
@CommonsLog
@CustomLog
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";

View File

@@ -1,7 +1,6 @@
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;
@@ -21,24 +20,14 @@ 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 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.CustomLog;
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.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.Identifier;
//- import net.minecraft.resources.ResourceLocation;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
@@ -46,9 +35,10 @@ import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.*;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponent;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
@CommonsLog
@CustomLog
@SuppressWarnings("unchecked")
public class TweedCoatMappersImpl {
public static TweedCoatMapper<Byte> BYTE_TEXT_MAPPER = convertingTextMapper(
@@ -93,10 +83,6 @@ 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,
@@ -105,240 +91,6 @@ 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;
@@ -504,12 +256,20 @@ public class TweedCoatMappersImpl {
CompoundConfigEntry<T> compoundEntry = (CompoundConfigEntry<T>) entry;
Optional<AttributesExtension> attributesExtension = entry.container().extension(AttributesExtension.class);
ResourceLocation backgroundTexture = attributesExtension
//# if MC_VERSION_NUMBER >= 260100
Identifier backgroundTexture = attributesExtension
//# else
//- ResourceLocation backgroundTexture = attributesExtension
//# end
.map(extension -> extension.getAttributeValue(
entry,
TweedCoatAttributes.BACKGROUND_TEXTURE
))
.map(ResourceLocation::tryParse)
//# if MC_VERSION_NUMBER >= 260100
.map(Identifier::tryParse)
//# else
//- .map(ResourceLocation::tryParse)
//# end
.orElse(null);
String translationKey = attributesExtension
.map(extension -> extension.getAttributeValue(

View File

@@ -1,18 +1,19 @@
package de.siphalor.tweed5.coat.bridge.testmod;
import de.siphalor.amecs.api.PriorityKeyBinding;
import de.siphalor.amecs.priority_key_mappings.api.AmecsPriorityKeyMapping;
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.serde.hjson.HjsonSerde;
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
import de.siphalor.tweed5.data.hjson.HjsonSerde;
import de.siphalor.tweed5.data.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.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
@@ -54,7 +55,11 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
config = configContainerHelper.loadAndUpdateInConfigDirectory(() -> DEFAULT_CONFIG_VALUE);
KeyBindingHelper.registerKeyBinding(new ScreenKeyBinding(
//# if MC_VERSION_NUMBER >= 260100
KeyMappingHelper.registerKeyMapping(new ScreenKeyBinding(
//# else
//- KeyBindingHelper.registerKeyBinding(new ScreenKeyBinding(
//# end
MOD_ID + ".config",
GLFW.GLFW_KEY_T,
//# if MC_VERSION_NUMBER >= 12109
@@ -67,7 +72,7 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
log.info("Current config: " + config);
}
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
private class ScreenKeyBinding extends KeyMapping implements AmecsPriorityKeyMapping {
//# if MC_VERSION_NUMBER >= 12109
public ScreenKeyBinding(String name, int key, Category category) {
//# else

View File

@@ -15,11 +15,12 @@ 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 = "minecraftModApiElements"))
testmodImplementation(project(":tweed5-logging", configuration = "minecraftModElements"))
testmodImplementation(project(":tweed5-bundle", configuration = "runtimeElements"))
testmodImplementation(project(":tweed5-bundle-pojo-weaving", configuration = "runtimeElements"))
testmodImplementation("de.siphalor.tweed5:tweed5-comment-loader-extension")

View File

@@ -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.serde.extension.api.ReadWriteExtension;
import de.siphalor.tweed5.data.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;

View File

@@ -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.serde_api.api.TweedDataReader;
import de.siphalor.tweed5.serde.gson.GsonReader;
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
import de.siphalor.tweed5.data.gson.GsonReader;
import lombok.Builder;
import lombok.extern.apachecommons.CommonsLog;
import lombok.CustomLog;
import org.jspecify.annotations.Nullable;
import java.io.InputStream;
import java.io.InputStreamReader;
@CommonsLog
@CustomLog
@Builder
public class FabricConfigCommentLoader {
private final ConfigContainer<?> configContainer;

View File

@@ -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 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.CustomLog;
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;
@CommonsLog
@CustomLog
public class FabricConfigContainerHelper<T extends @Nullable Object> {
@Getter
private final ConfigContainer<T> configContainer;
@@ -84,24 +84,14 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
return;
}
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
readContextCustomizer.accept(contextExtensionsData);
PatchInfo patchInfo = patchExtension.collectPatchInfo(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");
}
T readValue = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData);
patchExtension.patch(configContainer.rootEntry(), value, readValue, patchInfo);
} catch (Exception e) {
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
}
@@ -118,54 +108,20 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
return defaultValueSupplier.get();
}
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
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();
return readWriteExtension.read(reader, configContainer.rootEntry(), contextExtensionsData);
} 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(Files.newOutputStream(tempConfigFile.toPath()))) {
try (TweedDataWriter writer = serde.createWriter(new FileOutputStream(tempConfigFile))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
readWriteExtension.write(

View File

@@ -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.serde.hjson.HjsonSerde;
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
import de.siphalor.tweed5.data.hjson.HjsonSerde;
import de.siphalor.tweed5.data.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;

View File

@@ -1,4 +1,3 @@
org.gradle.jvmargs = -Xmx2G
org.gradle.configuration-cache = true
minecraft.version.descriptor = 1.21.10
minecraft.version.descriptor = 26.1.0

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 11605

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.16.5", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.16.5", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 11701

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.17.1", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.17.1", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 11802

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.18.2", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.18.2", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 11904

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.19.4", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.19.4", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 12006

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.20.2", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.20.2", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -1 +1,3 @@
fabric.api.key_mapping = fabric-key-binding-api-v1
preprocessor.mc_version_number = 12110

View File

@@ -1,12 +1,17 @@
[versions]
amecs-api = "1.6.2"
amecs-priorityKeyMappings = "1.0.1"
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-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.21.9", version.ref = "amecs-api" }
amecs-priorityKeyMappings = { group = "de.siphalor.amecs.amecs-priority-key-mappings", name = "amecs-priority-key-mappings-mc1.21.9", version.ref = "amecs-priorityKeyMappings" }
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" }

View File

@@ -0,0 +1,3 @@
fabric.api.key_mapping = fabric-key-mapping-api-v1
preprocessor.mc_version_number = 260100

View File

@@ -0,0 +1,16 @@
[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" }

View File

@@ -1,11 +1,11 @@
[versions]
fabric-loader = "0.17.2"
fabric-loom = "1.11-SNAPSHOT"
jcyo = "0.6.1"
fabric-loader = "0.18.5"
jcyo = "0.5.1"
smcmtk = "0.1.0"
[plugins]
jcyo = { id = "de.siphalor.jcyo", version.ref = "jcyo" }
fabric-loom = { id = "fabric-loom", version.ref = "fabric-loom" }
smcmtk = { id = "de.siphalor.minecraft-modding-toolkit.project-plugin", version.ref = "smcmtk" }
[libraries]
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }

View File

@@ -1,2 +1,7 @@
lombok.accessors.fluent = true
lombok.addLombokGeneratedAnnotation = 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

View File

@@ -1,4 +0,0 @@
# 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

View File

@@ -1,13 +0,0 @@
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")
}

View File

@@ -1,2 +0,0 @@
module.name = Tweed 5 Netwoking
module.description = Minecraft networking support for Tweed 5

View File

@@ -1,297 +0,0 @@
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;
}
}
}

View File

@@ -1,109 +0,0 @@
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() {
}
}

View File

@@ -1,87 +0,0 @@
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);
}
}
}

View File

@@ -1,36 +0,0 @@
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;
}

View File

@@ -1,4 +0,0 @@
@NullMarked
package de.siphalor.tweed5.minecraft.networking.impl;
import org.jspecify.annotations.NullMarked;

View File

@@ -1,157 +0,0 @@
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)
)
);
}
}

View File

@@ -44,7 +44,7 @@ dependencyResolutionManagement {
}
create("mcLibs") {
val mcVersionDescriptor = providers.gradleProperty("minecraft.version.descriptor").get()
from(files("gradle/mc-$mcVersionDescriptor/mcLibs.versions.toml"))
from(files("gradle/mc-$mcVersionDescriptor/mc.versions.toml"))
}
}
}
@@ -56,7 +56,6 @@ includeNormalModule("bundle-pojo-weaving")
includeNormalModule("coat-bridge")
includeNormalModule("fabric-helper")
includeNormalModule("logging")
includeNormalModule("networking")
fun includeNormalModule(name: String) {
includeAs("tweed5-$name", name)

View File

@@ -6,7 +6,6 @@ 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"))
}

View File

@@ -9,20 +9,17 @@ 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;
@@ -41,8 +38,6 @@ 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<>();
@@ -160,7 +155,7 @@ public class AttributesReadWriteFilterExtensionImpl
}
}
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
@Override
public String id() {
return EXTENSION_ID;
@@ -177,28 +172,32 @@ public class AttributesReadWriteFilterExtensionImpl
}
@Override
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
assert readWriteContextDataAccess != null;
//noinspection unchecked
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted
= (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
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);
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;
}
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<?, ?>, WriterMiddlewareContext> {
private class WriterMiddleware implements Middleware<TweedEntryWriter<?, ?>> {
@Override
public String id() {
return EXTENSION_ID;
@@ -215,18 +214,18 @@ public class AttributesReadWriteFilterExtensionImpl
}
@Override
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
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, writeContext) -> {
ReadWriteContextCustomData contextData = writeContext.extensionsData()
(writer, value, entry, context) -> {
ReadWriteContextCustomData contextData = context.extensionsData()
.get(readWriteContextDataAccess);
if (contextData == null || contextData.attributeFilters().isEmpty()) {
innerCasted.write(writer, value, entry, writeContext);
innerCasted.write(writer, value, entry, context);
return;
}
@@ -236,7 +235,7 @@ public class AttributesReadWriteFilterExtensionImpl
}
if (doFiltersMatch(entry, contextData)) {
innerCasted.write(writer, value, entry, writeContext);
innerCasted.write(writer, value, entry, context);
} else {
try {
writer.visitValue(TWEED_DATA_NOTHING_VALUE);
@@ -381,6 +380,5 @@ public class AttributesReadWriteFilterExtensionImpl
private static class ReadWriteContextCustomData {
private final Map<String, Set<String>> attributeFilters = new HashMap<>();
private boolean writerInstalled;
private boolean noopHandlerInstalled;
}
}

View File

@@ -8,11 +8,10 @@ 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.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 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 org.jspecify.annotations.NullUnmarked;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -27,9 +26,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.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.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.testutils.generic.MapTestUtils.sequencedMap;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
@@ -165,21 +164,19 @@ class AttributesReadWriteFilterExtensionImplTest {
}
}
""")));
TweedReadResult<Map<String, Object>> readResult = configContainer.rootEntry().call(read(
Map<String, Object> readValue = configContainer.rootEntry().call(read(
reader,
patchwork -> attributesReadWriteFilterExtension.addFilter(patchwork, "type", "a")
));
assertThat(readResult)
.extracting(TweedReadResult::value)
.asInstanceOf(map(String.class, Object.class))
assertThat(readValue)
.containsEntry("first", "1st")
.doesNotContainKey("second")
.containsEntry("second", null)
.hasEntrySatisfying(
"nested", nested -> assertThat(nested)
.asInstanceOf(map(String.class, Object.class))
.containsEntry("first", "n 1st")
.doesNotContainKey("second")
.containsEntry("second", null)
);
}
}

View File

@@ -1,88 +0,0 @@
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();
}
}

View File

@@ -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.serde_api.api.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
public interface CommentLoaderExtension extends TweedExtension {
Class<? extends CommentLoaderExtension> DEFAULT = CommentLoaderExtensionImpl.class;

View File

@@ -6,12 +6,11 @@ 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.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.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.CommentExtension;
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
@@ -45,8 +44,8 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
}
@Override
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
return new Middleware<CommentProducer, CommentProducerMiddlewareContext>() {
public Middleware<CommentProducer> commentMiddleware() {
return new Middleware<CommentProducer>() {
@Override
public String id() {
return EXTENSION_ID;
@@ -63,10 +62,7 @@ public class CommentLoaderExtensionImpl implements CommentLoaderExtension, Comme
}
@Override
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
if (context.entry().extensionsData().get(loadedCommentAccess) == null) {
return inner;
}
public CommentProducer process(CommentProducer inner) {
return entry -> {
String loadedComment = entry.extensionsData().get(loadedCommentAccess);
String innerComment = inner.createComment(entry);

View File

@@ -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.serde.gson.GsonReader;
import de.siphalor.tweed5.data.gson.GsonReader;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;

View File

@@ -1,13 +0,0 @@
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);
}

View File

@@ -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) {
SubEntryKey subEntryKey = SubEntryKey.structured("element", Integer.toString(index));
if (visitor.enterSubEntry(subEntryKey)) {
String indexString = Integer.toString(index);
if (visitor.enterStructuredSubEntry("element", indexString)) {
elementEntry().visitInOrder(visitor, item);
visitor.leaveSubEntry(subEntryKey);
visitor.leaveStructuredSubEntry("element", indexString);
}
index++;
}

View File

@@ -4,13 +4,18 @@ import de.siphalor.tweed5.core.api.Arity;
import java.util.function.Consumer;
public interface CompoundConfigEntry<T> extends MutableStructuredConfigEntry<T> {
public interface CompoundConfigEntry<T> extends StructuredConfigEntry<T> {
@Override
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
MutableStructuredConfigEntry.super.apply(function);
StructuredConfigEntry.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)) {

View File

@@ -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(StructuredConfigEntry<T> entry, T value) {
default <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
return true;
}
default boolean enterSubEntry(SubEntryKey subEntryKey) {
default boolean enterStructuredSubEntry(String entryKey, String valueKey) {
return true;
}
default void leaveSubEntry(SubEntryKey subEntryKey) {
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
}
default <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
default <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
}
}

View File

@@ -1,16 +0,0 @@
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);
}

View File

@@ -5,12 +5,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public interface NullableConfigEntry<T extends @Nullable Object> extends AddressableStructuredConfigEntry<T> {
public interface NullableConfigEntry<T extends @Nullable Object> extends StructuredConfigEntry<T> {
String NON_NULL_KEY = ":nonNull";
@Override
default NullableConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
AddressableStructuredConfigEntry.super.apply(function);
StructuredConfigEntry.super.apply(function);
return this;
}

View File

@@ -1,53 +0,0 @@
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);
}
}

View File

@@ -8,11 +8,11 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M, C> {
public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
private static final String CONTAINER_ID = "";
@Getter
private List<Middleware<M, C>> middlewares = new ArrayList<>();
private List<Middleware<M>> middlewares = new ArrayList<>();
private final Set<String> middlewareIds = new HashSet<>();
private boolean sealed = false;
@@ -22,10 +22,10 @@ public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M,
}
@Override
public void registerAll(Collection<Middleware<M, C>> middlewares) {
public void registerAll(Collection<Middleware<M>> middlewares) {
requireUnsealed();
for (Middleware<M, C> middleware : middlewares) {
for (Middleware<M> middleware : middlewares) {
if (middleware.id().isEmpty()) {
throw new IllegalArgumentException("Middleware id cannot be empty");
}
@@ -37,7 +37,7 @@ public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M,
}
@Override
public void register(Middleware<M, C> middleware) {
public void register(Middleware<M> middleware) {
requireUnsealed();
if (middleware.id().isEmpty()) {
@@ -76,7 +76,7 @@ public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M,
AcyclicGraphSorter sorter = new AcyclicGraphSorter(allMentionedMiddlewareIds.length);
for (Middleware<M, C> middleware : middlewares) {
for (Middleware<M> middleware : middlewares) {
Integer currentIndex = indecesByMiddlewareId.get(middleware.id());
middleware.mustComeAfter().stream()
@@ -87,8 +87,7 @@ public class DefaultMiddlewareContainer<M, C> implements MiddlewareContainer<M,
.forEach(afterIndex -> sorter.addEdge(currentIndex, afterIndex));
}
Map<String, Middleware<M, C>> middlewaresById = middlewares.stream()
.collect(Collectors.toMap(Middleware::id, Function.identity()));
Map<String, Middleware<M>> middlewaresById = middlewares.stream().collect(Collectors.toMap(Middleware::id, Function.identity()));
try {
int[] sortedIndeces = sorter.sort();
@@ -108,27 +107,13 @@ public class DefaultMiddlewareContainer<M, C> 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, C> middleware = middlewares.get(i);
Middleware<M> middleware = middlewares.get(i);
combined = middleware.process(combined);
}
return combined;

View File

@@ -1,11 +1,9 @@
package de.siphalor.tweed5.core.api.middleware;
import org.jetbrains.annotations.ApiStatus;
import java.util.Collections;
import java.util.Set;
public interface Middleware<M, C> {
public interface Middleware<M> {
String DEFAULT_START = "$default.start";
String DEFAULT_END = "$default.end";
@@ -18,13 +16,5 @@ public interface Middleware<M, C> {
return Collections.singleton(DEFAULT_START);
}
default M process(M inner, C context) {
return process(inner);
}
@Deprecated
@ApiStatus.OverrideOnly
default M process(M inner) {
return inner;
}
M process(M inner);
}

View File

@@ -2,11 +2,11 @@ package de.siphalor.tweed5.core.api.middleware;
import java.util.Collection;
public interface MiddlewareContainer<M, C> extends Middleware<M, C> {
default void registerAll(Collection<Middleware<M, C>> middlewares) {
public interface MiddlewareContainer<M> extends Middleware<M> {
default void registerAll(Collection<Middleware<M>> middlewares) {
middlewares.forEach(this::register);
}
void register(Middleware<M, C> middleware);
void register(Middleware<M> middleware);
void seal();
Collection<Middleware<M, C>> middlewares();
Collection<Middleware<M>> middlewares();
}

View File

@@ -2,12 +2,11 @@ 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 @NonNull Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
private final IntFunction<T> collectionConstructor;
private final ConfigEntry<E> elementEntry;

View File

@@ -5,7 +5,6 @@ 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;
@@ -25,11 +24,6 @@ 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);
@@ -39,10 +33,9 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (value != null) {
if (visitor.enterStructuredEntry(this, value)) {
SubEntryKey subEntryKey = SubEntryKey.transparentAddressable(NON_NULL_KEY, NON_NULL_KEY);
if (visitor.enterSubEntry(subEntryKey)) {
if (visitor.enterStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY)) {
nonNullEntry.visitInOrder(visitor, value);
visitor.leaveSubEntry(subEntryKey);
visitor.leaveStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY);
}
visitor.leaveStructuredEntry(this, value);
}

View File

@@ -2,13 +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.LinkedHashMap;
import java.util.Map;
import java.util.function.IntFunction;
public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
private final IntFunction<T> mapConstructor;
private final Map<String, ConfigEntry<?>> compoundEntries;
@@ -29,15 +28,16 @@ public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Obj
}
@Override
public void set(T compoundValue, String dataKey, Object value) {
requireKey(dataKey);
compoundValue.put(dataKey, value);
public <V> void set(T compoundValue, String key, V value) {
requireKey(key);
compoundValue.put(key, value);
}
@Override
public Object get(T compoundValue, String dataKey) {
requireKey(dataKey);
return compoundValue.get(dataKey);
public <V> V get(T compoundValue, String key) {
requireKey(key);
//noinspection unchecked
return (V) compoundValue.get(key);
}
private void requireKey(String key) {
@@ -47,28 +47,29 @@ public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Obj
}
@Override
public T instantiateValue() {
public T instantiateCompoundValue() {
return mapConstructor.apply(compoundEntries.size());
}
@Override
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterStructuredEntry(this, value)) {
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);
}
});
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);
}
});
}
visitor.leaveStructuredEntry(this, value);
}
}
@Override
public T deepCopy(T value) {
T copy = instantiateValue();
T copy = instantiateCompoundValue();
value.forEach((String key, Object element) -> {
//noinspection unchecked
ConfigEntry<Object> elementEntry = (ConfigEntry<Object>) compoundEntries.get(key);

View File

@@ -3,5 +3,5 @@ package de.siphalor.tweed5.defaultextensions.comment.api;
import de.siphalor.tweed5.core.api.middleware.Middleware;
public interface CommentModifyingExtension {
Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware();
Middleware<CommentProducer> commentMiddleware();
}

View File

@@ -1,14 +0,0 @@
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;
}

View File

@@ -6,9 +6,8 @@ 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.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.data.extension.api.extension.ReadWriteExtensionSetupContext;
import de.siphalor.tweed5.data.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;
@@ -21,7 +20,7 @@ public class CommentExtensionImpl implements ReadWriteRelatedExtension, CommentE
private final ConfigContainer<?> configContainer;
@Getter
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
private final DefaultMiddlewareContainer<CommentProducer, CommentProducerMiddlewareContext> middlewareContainer;
private final DefaultMiddlewareContainer<CommentProducer> middlewareContainer;
@Getter
private @Nullable PatchworkPartAccess<Boolean> writerInstalledReadWriteContextAccess;
@@ -72,10 +71,7 @@ public class CommentExtensionImpl implements ReadWriteRelatedExtension, CommentE
public void recomputeFullComments() {
configContainer.rootEntry().visitInOrder(entry -> {
CustomEntryData entryData = getOrCreateCustomEntryData(entry);
entryData.commentProducer(middlewareContainer.process(
_entry -> entryData.baseComment(),
CommentProducerMiddlewareContext.builder().entry(entry).build()
));
entryData.commentProducer(middlewareContainer.process(_entry -> entryData.baseComment()));
});
}

View File

@@ -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.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.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.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<?, ?>, WriterMiddlewareContext> {
class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?, ?>> {
private final CommentExtensionImpl commentExtension;
@Override
@@ -27,15 +27,15 @@ class TweedEntryWriterCommentMiddleware implements Middleware<TweedEntryWriter<?
}
@Override
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
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, writeContext) -> {
if (!Boolean.TRUE.equals(writeContext.extensionsData().get(writerInstalledAccess))) {
writeContext.extensionsData().set(writerInstalledAccess, Boolean.TRUE);
return (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) (writer, value, entry, context) -> {
if (!Boolean.TRUE.equals(context.extensionsData().get(writerInstalledAccess))) {
context.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, writeContext);
innerCasted.write(writer, value, entry, context);
};
}

View File

@@ -3,11 +3,12 @@ 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.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.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.patch.api.PatchExtension;
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
import de.siphalor.tweed5.patchwork.api.Patchwork;
@@ -61,7 +62,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
if (targetValue != null) {
targetCompoundValue = targetValue;
} else {
targetCompoundValue = compoundEntry.instantiateValue();
targetCompoundValue = compoundEntry.instantiateCompoundValue();
}
compoundEntry.subEntries().forEach((key, subEntry) -> {
if (!patchInfo.containsEntry(subEntry)) {
@@ -69,7 +70,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
}
compoundEntry.set(
targetCompoundValue, key, patch(
(ConfigEntry<Object>) subEntry,
subEntry,
compoundEntry.get(targetCompoundValue, key),
compoundEntry.get(patchValue, key),
patchInfo
@@ -82,26 +83,33 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
}
}
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
private class ReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
@Override
public String id() {
return "patch-info-collector";
}
@Override
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
assert readWriteContextDataAccess != null;
//noinspection unchecked
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted =
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
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 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 readResult;
};
}
}

View File

@@ -9,11 +9,7 @@ public interface PathTracking {
void pushPathPart(String pathPart);
void pushEmptyPathPart();
void popPathPart();
String currentPath();
String[] currentPathParts();
}

View File

@@ -2,8 +2,6 @@ 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;
@@ -18,31 +16,27 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
}
@Override
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
return delegate.enterStructuredEntry(entry, value);
}
@Override
public boolean enterSubEntry(SubEntryKey subEntryKey) {
boolean enter = delegate.enterSubEntry(subEntryKey);
public boolean enterStructuredSubEntry(String entryKey, String valueKey) {
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey);
if (enter) {
if (subEntryKey.value() != null) {
pathTracking.pushPathPart(subEntryKey.entry(), subEntryKey.value());
} else {
pathTracking.pushEmptyValuePathPart(subEntryKey.entry());
}
pathTracking.pushPathPart(entryKey, valueKey);
}
return enter;
}
@Override
public void leaveSubEntry(SubEntryKey subEntryKey) {
delegate.leaveSubEntry(subEntryKey);
public void leaveStructuredSubEntry(String entryKey, String valueKey) {
delegate.leaveStructuredSubEntry(entryKey, valueKey);
pathTracking.popPathPart();
}
@Override
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
delegate.leaveStructuredEntry(entry, value);
}
}

View File

@@ -1,8 +1,8 @@
package de.siphalor.tweed5.defaultextensions.pather.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.dataapi.api.TweedDataReadException;
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
import lombok.RequiredArgsConstructor;
import java.util.ArrayDeque;

View File

@@ -1,8 +1,8 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
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.dataapi.api.TweedDataUnsupportedValueException;
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.Nullable;

View File

@@ -1,8 +1,8 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
import de.siphalor.tweed5.core.api.extension.TweedExtension;
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
import de.siphalor.tweed5.defaultextensions.pather.impl.PatherExtensionImpl;
public interface PatherExtension extends TweedExtension {

View File

@@ -17,17 +17,6 @@ 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();
@@ -42,13 +31,4 @@ 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();
}
}

View File

@@ -1,14 +1,13 @@
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<@Nullable String> pathParts = new ArrayDeque<>(50);
private final Deque<String> pathParts = new ArrayDeque<>(50);
@Override
public void pushPathPart(String entryPathPart) {
@@ -16,18 +15,11 @@ 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();
if (poppedPart != null) {
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
}
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
}
}
@@ -35,9 +27,4 @@ public class PathTrackingImpl implements PathTracking {
public String currentPath() {
return pathBuilder.toString();
}
@Override
public String[] currentPathParts() {
return pathParts.toArray(new String[0]);
}
}

View File

@@ -2,13 +2,11 @@ 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.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.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.defaultextensions.pather.api.PathTracking;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataReader;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataVisitor;
@@ -50,62 +48,75 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
return pathTracking.currentPath();
}
private Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> createEntryReaderMiddleware() {
return new Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext>() {
private Middleware<TweedEntryReader<?, ?>> createEntryReaderMiddleware() {
return new Middleware<TweedEntryReader<?, ?>>() {
@Override
public String id() {
return EXTENSION_ID;
}
@Override
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
assert rwContextPathTrackingAccess != null;
//noinspection unchecked
val castedInner = (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext readContext) -> {
PathTracking pathTracking = readContext.extensionsData().get(rwContextPathTrackingAccess);
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
PathTracking pathTracking = context.extensionsData().get(rwContextPathTrackingAccess);
if (pathTracking != null) {
return castedInner.read(reader, entry, readContext);
return castedInner.read(reader, entry, context);
}
pathTracking = PathTracking.create();
readContext.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, readContext);
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;
}
}
};
}
};
}
private Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext> createEntryWriterMiddleware() {
return new Middleware<TweedEntryWriter<?, ?>, WriterMiddlewareContext>() {
private Middleware<TweedEntryWriter<?, ?>> createEntryWriterMiddleware() {
return new Middleware<TweedEntryWriter<?, ?>>() {
@Override
public String id() {
return EXTENSION_ID;
}
@Override
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner, WriterMiddlewareContext context) {
public TweedEntryWriter<?, ?> process(TweedEntryWriter<?, ?> inner) {
assert rwContextPathTrackingAccess != null;
//noinspection unchecked
val castedInner = (TweedEntryWriter<Object, @NonNull ConfigEntry<Object>>) inner;
return (TweedDataVisitor writer, Object value, ConfigEntry<Object> entry, TweedWriteContext writeContext) -> {
PathTracking pathTracking = writeContext.extensionsData().get(rwContextPathTrackingAccess);
return (TweedDataVisitor writer, Object value, ConfigEntry<Object> entry, TweedWriteContext context) -> {
PathTracking pathTracking = context.extensionsData().get(rwContextPathTrackingAccess);
if (pathTracking != null) {
castedInner.write(writer, value, entry, writeContext);
castedInner.write(writer, value, entry, context);
return;
}
pathTracking = PathTracking.create();
writeContext.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
try {
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, writeContext);
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
} catch (TweedEntryWriteException e) {
PathTracking exceptionPathTracking =
e.context().extensionsData().get(rwContextPathTrackingAccess);
val exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
if (exceptionPathTracking != null) {
throw new TweedEntryWriteException(
"Exception while writing entry at "

View File

@@ -3,15 +3,14 @@ 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.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.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.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;
@@ -31,7 +30,9 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
PresetsExtension presetsExtension = configContainer.extension(PresetsExtension.class)
.orElseThrow(() -> new IllegalStateException(getClass().getSimpleName()
+ " requires " + ReadFallbackExtension.class.getSimpleName()));
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext>() {
PatherExtension patherExtension = configContainer.extension(PatherExtension.class).orElse(null);
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>>() {
@Override
public String id() {
return EXTENSION_ID;
@@ -48,19 +49,26 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
}
@Override
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
//noinspection unchecked
TweedEntryReader<Object, ConfigEntry<Object>> castedInner =
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
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
);
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);
}
};
}
});
}

View File

@@ -8,7 +8,6 @@ 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.*;
@@ -56,10 +55,7 @@ public interface ValidationExtension extends TweedExtension {
lastId = id;
}
}
<T> void addValidatorMiddleware(
ConfigEntry<T> entry,
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator
);
<T> void addValidatorMiddleware(ConfigEntry<T> entry, Middleware<ConfigEntryValidator> validator);
ValidationIssues captureValidationIssues(Patchwork readContextExtensionsData);

View File

@@ -3,5 +3,5 @@ package de.siphalor.tweed5.defaultextensions.validation.api;
import de.siphalor.tweed5.core.api.middleware.Middleware;
public interface ValidationProvidingExtension {
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validationMiddleware();
Middleware<ConfigEntryValidator> validationMiddleware();
}

View File

@@ -1,13 +0,0 @@
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;
}

View File

@@ -3,7 +3,6 @@ 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;
@@ -11,12 +10,12 @@ import org.jspecify.annotations.Nullable;
@Getter
@AllArgsConstructor
public class SimpleValidatorMiddleware implements Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> {
public class SimpleValidatorMiddleware implements Middleware<ConfigEntryValidator> {
String id;
ConfigEntryValidator validator;
@Override
public ConfigEntryValidator process(ConfigEntryValidator inner, ValidatorMiddlewareContext context) {
public ConfigEntryValidator process(ConfigEntryValidator inner) {
return new ConfigEntryValidator() {
@Override
public <T extends @Nullable Object> ValidationResult<T> validate(ConfigEntry<T> configEntry, T value) {

View File

@@ -3,15 +3,19 @@ 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;
@@ -19,23 +23,13 @@ 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.*;
@@ -74,8 +68,8 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
private final ConfigContainer<?> configContainer;
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
private final MiddlewareContainer<ConfigEntryValidator, ValidatorMiddlewareContext>
entryValidatorMiddlewareContainer = new DefaultMiddlewareContainer<>();
private final MiddlewareContainer<ConfigEntryValidator> entryValidatorMiddlewareContainer
= new DefaultMiddlewareContainer<>();
private @Nullable PatchworkPartAccess<ValidationIssues> readContextValidationIssuesAccess;
private @Nullable PatherExtension patherExtension;
@@ -101,15 +95,15 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
@Override
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
return new Middleware<CommentProducer, CommentProducerMiddlewareContext>() {
public Middleware<CommentProducer> commentMiddleware() {
return new Middleware<CommentProducer>() {
@Override
public String id() {
return EXTENSION_ID;
}
@Override
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
public CommentProducer process(CommentProducer inner) {
return entry -> {
String baseComment = inner.createComment(entry);
CustomEntryData entryData = entry.extensionsData().get(customEntryDataAccess);
@@ -139,10 +133,7 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
@Override
public <T> void addValidatorMiddleware(
ConfigEntry<T> entry,
Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator
) {
public <T> void addValidatorMiddleware(ConfigEntry<T> entry, Middleware<ConfigEntryValidator> validator) {
CustomEntryData entryData = getOrCreateCustomEntryData(entry);
entryData.addValidator(validator);
}
@@ -159,20 +150,13 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
CustomEntryData entryData = getOrCreateCustomEntryData(configEntry);
if (entryData.validators().isEmpty()) {
entryData.completeValidator(entryValidatorMiddlewareContainer.process(
baseValidator,
ValidatorMiddlewareContext.builder().entry(configEntry).build()
));
entryData.completeValidator(entryValidatorMiddlewareContainer.process(baseValidator));
} else {
DefaultMiddlewareContainer<ConfigEntryValidator, ValidatorMiddlewareContext> entrySpecificValidatorContainer
= new DefaultMiddlewareContainer<>();
DefaultMiddlewareContainer<ConfigEntryValidator> entrySpecificValidatorContainer = new DefaultMiddlewareContainer<>();
entrySpecificValidatorContainer.registerAll(entryValidatorMiddlewareContainer.middlewares());
entrySpecificValidatorContainer.registerAll(entryData.validators());
entrySpecificValidatorContainer.seal();
entryData.completeValidator(entrySpecificValidatorContainer.process(
baseValidator,
ValidatorMiddlewareContext.builder().entry(configEntry).build()
));
entryData.completeValidator(entrySpecificValidatorContainer.process(baseValidator));
}
}
@@ -213,14 +197,14 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
@Data
private static class CustomEntryData {
@Setter(AccessLevel.NONE)
private @Nullable List<Middleware<ConfigEntryValidator, ValidatorMiddlewareContext>> validators;
private @Nullable List<Middleware<ConfigEntryValidator>> validators;
private @Nullable ConfigEntryValidator completeValidator;
public List<Middleware<ConfigEntryValidator, ValidatorMiddlewareContext>> validators() {
public List<Middleware<ConfigEntryValidator>> validators() {
return validators == null ? Collections.emptyList() : validators;
}
public void addValidator(Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validator) {
public void addValidator(Middleware<ConfigEntryValidator> validator) {
if (validators == null) {
validators = new ArrayList<>();
}
@@ -228,8 +212,7 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
}
private class EntryValidationReaderMiddleware
implements Middleware<TweedEntryReader<?, ?>, ReaderMiddlewareContext> {
private class EntryValidationReaderMiddleware implements Middleware<TweedEntryReader<?, ?>> {
@Override
public String id() {
return EXTENSION_ID;
@@ -241,55 +224,43 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
@Override
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner, ReaderMiddlewareContext context) {
public TweedEntryReader<?, ?> process(TweedEntryReader<?, ?> inner) {
assert readContextValidationIssuesAccess != null && patherExtension != null;
//noinspection unchecked
TweedEntryReader<Object, ConfigEntry<Object>> castedInner = (TweedEntryReader<Object, ConfigEntry<Object>>) inner;
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext readContext) -> {
ValidationIssues validationIssues = getOrCreateValidationIssues(readContext.extensionsData());
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
ValidationIssues validationIssues = getOrCreateValidationIssues(context.extensionsData());
return castedInner.read(reader, entry, readContext).andThen(value -> {
ConfigEntryValidator entryValidator = entry.extensionsData()
.get(customEntryDataAccess)
.completeValidator();
assert entryValidator != null;
Object value = castedInner.read(reader, entry, context);
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
ConfigEntryValidator entryValidator = entry.extensionsData()
.get(customEntryDataAccess)
.completeValidator();
assert entryValidator != null;
if (validationResult.issues().isEmpty()) {
return TweedReadResult.ok(validationResult.value());
}
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
String path = patherExtension.getPath(readContext);
if (!validationResult.issues().isEmpty()) {
String path = patherExtension.getPath(context);
validationIssues.issuesByPath().put(path, new ValidationIssues.EntryIssues(
entry,
validationResult.issues()
));
if (validationResult.hasError()) {
return TweedReadResult.failed(
mapValidationIssuesToReadIssues(validationResult.issues(), readContext)
);
} else {
return TweedReadResult.withIssues(
validationResult.value(),
mapValidationIssuesToReadIssues(validationResult.issues(), readContext)
);
}
}, readContext);
}
if (validationResult.hasError()) {
throw new TweedEntryReadException(
"Failed to validate entry: " + validationResult.issues(),
context
);
}
return validationResult.value();
};
}
}
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);
@@ -319,12 +290,12 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
}
@Override
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
return true;
}
@Override
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
visitEntry(entry, value);
}
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -63,11 +62,11 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
}
@Override
public Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> validationMiddleware() {
public Middleware<ConfigEntryValidator> validationMiddleware() {
return new ValidationFallbackMiddleware();
}
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator, ValidatorMiddlewareContext> {
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator> {
@Override
public String id() {
return EXTENSION_ID;
@@ -84,7 +83,7 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
}
@Override
public ConfigEntryValidator process(ConfigEntryValidator inner, ValidatorMiddlewareContext context) {
public ConfigEntryValidator process(ConfigEntryValidator inner) {
return new ConfigEntryValidator() {
@Override
public <T extends @Nullable Object> ValidationResult<T> validate(ConfigEntry<T> configEntry, T value) {

View File

@@ -9,10 +9,9 @@ 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.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.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.CommentExtension;
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
@@ -27,9 +26,9 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.entryReaderWriter;
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.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.*;
@@ -145,7 +144,7 @@ class CommentExtensionImplTest {
}
@Override
public Middleware<CommentProducer, CommentProducerMiddlewareContext> commentMiddleware() {
public Middleware<CommentProducer> commentMiddleware() {
return new Middleware<>() {
@Override
public String id() {
@@ -153,7 +152,7 @@ class CommentExtensionImplTest {
}
@Override
public CommentProducer process(CommentProducer inner, CommentProducerMiddlewareContext context) {
public CommentProducer process(CommentProducer inner) {
return entry -> "The comment is:\n" + inner.createComment(entry) + "\nEND";
}
};

View File

@@ -8,10 +8,9 @@ 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.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.data.extension.api.ReadWriteExtension;
import de.siphalor.tweed5.data.hjson.HjsonLexer;
import de.siphalor.tweed5.data.hjson.HjsonReader;
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
import lombok.SneakyThrows;
@@ -27,9 +26,9 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
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.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.testutils.generic.MapTestUtils.sequencedMap;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
@@ -151,7 +150,7 @@ class PatchExtensionImplTest {
PatchExtension patchExtension = configContainer.extension(PatchExtension.class).orElseThrow();
var patchInfo = new AtomicReference<@Nullable PatchInfo>();
TweedReadResult<Map<String, Object>> patchResult = rootEntry.call(read(
Map<String, Object> patchValue = rootEntry.call(read(
reader, extensionsData ->
patchInfo.set(patchExtension.collectPatchInfo(extensionsData))
));
@@ -159,7 +158,7 @@ class PatchExtensionImplTest {
Map<String, Object> resultValue = patchExtension.patch(
rootEntry,
baseValue,
patchResult.value(),
patchValue,
Objects.requireNonNull(patchInfo.get())
);

View File

@@ -1,8 +1,8 @@
package de.siphalor.tweed5.defaultextensions.pather.api;
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.dataapi.api.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataToken;
import de.siphalor.tweed5.dataapi.api.TweedDataTokens;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

View File

@@ -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,11 +34,12 @@ 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
@@ -58,18 +59,12 @@ class ReadFallbackExtensionImplTest {
configContainer.attachTree(entry);
configContainer.initialize();
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12"))))))
.isEqualTo(TweedReadResult.ok(12));
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12")))))).isEqualTo(12);
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
logsCaptor.clear();
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")
);
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).isEqualTo(-1);
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).hasSize(1);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@@ -110,7 +105,6 @@ 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);
@@ -120,20 +114,22 @@ 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)).isEmpty();
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).singleElement()
.extracting(ILoggingEvent::getMessage)
.asInstanceOf(STRING)
.contains("first.second");
}
private static class EvenIntReader implements TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> {
@Override
public TweedReadResult<Integer> read(
public Integer read(
TweedDataReader reader,
ConfigEntry<Integer> entry,
TweedReadContext context
) {
) throws TweedEntryReadException {
int value;
try {
value = reader.readToken().readAsInt();
@@ -141,9 +137,9 @@ class ReadFallbackExtensionImplTest {
throw new IllegalStateException("Should not be called", e);
}
if (value % 2 == 0) {
return TweedReadResult.ok(value);
return value;
} else {
return TweedReadResult.error(TweedReadIssue.error("Value is not even", context));
throw new TweedEntryReadException("Value is not even", context);
}
}

View File

@@ -6,12 +6,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.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.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.defaultextensions.comment.api.CommentExtension;
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
@@ -32,10 +31,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;
@@ -157,14 +156,12 @@ class ValidationExtensionImplTest {
}
""")));
var validationIssues = new AtomicReference<@Nullable ValidationIssues>();
TweedReadResult<Map<String, Object>> value = configContainer.rootEntry().call(read(
Map<String, Object> value = configContainer.rootEntry().call(read(
reader,
extensionsData -> validationIssues.set(validationExtension.captureValidationIssues(extensionsData))
));
assertThat(value)
.extracting(TweedReadResult::value)
.isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
assertThat(value).isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
//noinspection DataFlowIssue
assertThat(validationIssues.get()).isNotNull().satisfies(
vi -> assertValidationIssue(

View File

@@ -4,12 +4,11 @@ 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.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.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.defaultextensions.comment.api.CommentExtension;
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
@@ -27,11 +26,10 @@ 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 {
@@ -116,9 +114,8 @@ class ValidationFallbackExtensionImplTest {
@ParameterizedTest
@ValueSource(strings = {"0", "7", "123", "null"})
void fallbackTriggers(String input) {
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);
Integer result = intEntry.call(read(new HjsonReader(new HjsonLexer(new StringReader(input)))));
assertEquals(3, result);
}
@Test

View File

@@ -1,2 +0,0 @@
org.gradle.jvmargs = -Xmx2G
org.gradle.configuration-cache = true

View File

@@ -1,2 +0,0 @@
lombok.accessors.fluent = true
lombok.addLombokGeneratedAnnotation = true

View File

@@ -1,6 +1,6 @@
package de.siphalor.tweed5.serde_api.api;
package de.siphalor.tweed5.dataapi.api;
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

Some files were not shown because too many files have changed in this diff Show More