refactor!(serde-extension): Fundamentally change error handling for data reading

This commit is contained in:
2026-04-04 22:33:15 +02:00
parent e6ef13ff7f
commit 615d3810a0
26 changed files with 706 additions and 403 deletions

View File

@@ -1,6 +1,7 @@
package de.siphalor.tweed5.coat.bridge.impl;
import de.siphalor.coat.handler.ConfigEntryHandler;
import de.siphalor.coat.handler.Message;
import de.siphalor.coat.input.CheckBoxConfigInput;
import de.siphalor.coat.input.ConfigInput;
import de.siphalor.coat.input.CycleButtonConfigInput;
@@ -20,22 +21,23 @@ 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.TweedEntryReadException;
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 de.siphalor.tweed5.patchwork.api.Patchwork;
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 org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
@@ -44,8 +46,7 @@ import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponent;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.*;
@CommonsLog
@SuppressWarnings("unchecked")
@@ -133,64 +134,69 @@ public class TweedCoatMappersImpl {
if (context.parentSaveHandler() == null) {
throw new IllegalArgumentException("No parent save handler provided");
}
return new ConvertingTweedCoatEntryHandler<>(
new BasicTweedCoatEntryHandler<>(entry, context.defaultValue(), context.parentSaveHandler()),
this::convertToString,
input -> {
try {
TweedEntryReader<T, ConfigEntry<T>> reader =
readWriteExtension.getDefinedEntryReader(entry);
Patchwork readExtData = readWriteExtension.createReadWriteContextExtensionsData();
//noinspection DataFlowIssue
return reader.read(
new TweedDataReader() {
private boolean consumed = false;
return new ConfigEntryHandler<String>() {
@Override
public String getDefault() {
return convertToString(context.defaultValue());
}
@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 input;
}
};
}
@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;
}
}
);
} catch (TweedEntryReadException e) {
throw new RuntimeException(e.getMessage(), e);
@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
@@ -270,6 +276,57 @@ public class TweedCoatMappersImpl {
}
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;
}
}
);
}
};
}
}

View File

@@ -58,9 +58,9 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
MOD_ID + ".config",
GLFW.GLFW_KEY_T,
//# if MC_VERSION_NUMBER >= 12109
//- KeyMapping.Category.MISC
KeyMapping.Category.MISC
//# else
"key.categories.misc"
//- "key.categories.misc"
//# end
));
@@ -69,9 +69,9 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
//# if MC_VERSION_NUMBER >= 12109
//- public ScreenKeyBinding(String name, int key, Category category) {
public ScreenKeyBinding(String name, int key, Category category) {
//# else
public ScreenKeyBinding(String name, int key, String category) {
//- public ScreenKeyBinding(String name, int key, String category) {
//# end
super(name, key, category);
}

View File

@@ -2,22 +2,22 @@ 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.serde.extension.api.ReadWriteExtension;
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 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.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;
@@ -84,14 +84,24 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
return;
}
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
readContextCustomizer.accept(contextExtensionsData);
PatchInfo patchInfo = patchExtension.collectPatchInfo(contextExtensionsData);
T readValue = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData);
TweedReadResult<T> readResult = readWriteExtension.read(reader, configContainer().rootEntry(), contextExtensionsData);
if (readResult.hasValue()) {
patchExtension.patch(configContainer.rootEntry(), value, readResult.value(), patchInfo);
if (readResult.hasIssues()) {
log.warn(formatIssuesLogMessage(configFile, "issues", readResult.issues()));
}
} else if (readResult.hasIssues()) {
log.error(formatIssuesLogMessage(configFile, "errors", readResult.issues()));
} else {
log.debug("Reading config file " + configFile.getAbsolutePath() + " yielded empty result");
}
patchExtension.patch(configContainer.rootEntry(), value, readValue, patchInfo);
} catch (Exception e) {
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
}
@@ -108,20 +118,54 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
return defaultValueSupplier.get();
}
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
return readWriteExtension.read(reader, configContainer.rootEntry(), contextExtensionsData);
TweedReadResult<T> readResult = readWriteExtension.read(
reader,
configContainer.rootEntry(),
contextExtensionsData
);
if (readResult.hasValue()) {
if (readResult.hasIssues()) {
log.warn(formatIssuesLogMessage(configFile, "issues", readResult.issues()));
}
return readResult.value();
} else if (readResult.hasIssues()) {
log.error(formatIssuesLogMessage(configFile, "errors", readResult.issues()));
} else {
log.info(
"Reading config file "
+ configFile.getAbsolutePath()
+ " yielded empty result, using default value"
);
}
return defaultValueSupplier.get();
} catch (Exception e) {
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
return defaultValueSupplier.get();
}
}
private String formatIssuesLogMessage(File file, String type, TweedReadIssue[] issues) {
String filePath = file.getAbsolutePath();
StringBuilder stringBuilder = new StringBuilder(20 + filePath.length() + type.length() + issues.length * 50);
stringBuilder.append("Encountered ");
stringBuilder.append(type);
stringBuilder.append(" while reading ");
stringBuilder.append(filePath);
stringBuilder.append(": ");
for (TweedReadIssue issue : issues) {
stringBuilder.append(" - ").append(issue).append("\n");
}
return stringBuilder.toString();
}
public void writeConfigInConfigDirectory(T configValue) {
File configFile = getConfigFile();
Path tempConfigDirectory = getOrCreateTempConfigDirectory();
File tempConfigFile = tempConfigDirectory.resolve(getConfigFileName()).toFile();
try (TweedDataWriter writer = serde.createWriter(new FileOutputStream(tempConfigFile))) {
try (TweedDataWriter writer = serde.createWriter(Files.newOutputStream(tempConfigFile.toPath()))) {
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
readWriteExtension.write(