refactor!(serde-extension): Fundamentally change error handling for data reading
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package de.siphalor.tweed5.coat.bridge.impl;
|
package de.siphalor.tweed5.coat.bridge.impl;
|
||||||
|
|
||||||
import de.siphalor.coat.handler.ConfigEntryHandler;
|
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||||
|
import de.siphalor.coat.handler.Message;
|
||||||
import de.siphalor.coat.input.CheckBoxConfigInput;
|
import de.siphalor.coat.input.CheckBoxConfigInput;
|
||||||
import de.siphalor.coat.input.ConfigInput;
|
import de.siphalor.coat.input.ConfigInput;
|
||||||
import de.siphalor.coat.input.CycleButtonConfigInput;
|
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.coat.bridge.api.mapping.handler.ConvertingTweedCoatEntryHandler;
|
||||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
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.ReadWriteExtension;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReadException;
|
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
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.TweedDataReadException;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
import de.siphalor.tweed5.serde_api.api.TweedDataToken;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import lombok.extern.apachecommons.CommonsLog;
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.resources.language.I18n;
|
import net.minecraft.client.resources.language.I18n;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@@ -44,8 +46,7 @@ import java.util.*;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponent;
|
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.*;
|
||||||
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
|
|
||||||
|
|
||||||
@CommonsLog
|
@CommonsLog
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -133,66 +134,71 @@ public class TweedCoatMappersImpl {
|
|||||||
if (context.parentSaveHandler() == null) {
|
if (context.parentSaveHandler() == null) {
|
||||||
throw new IllegalArgumentException("No parent save handler provided");
|
throw new IllegalArgumentException("No parent save handler provided");
|
||||||
}
|
}
|
||||||
return new ConvertingTweedCoatEntryHandler<>(
|
return new ConfigEntryHandler<String>() {
|
||||||
new BasicTweedCoatEntryHandler<>(entry, context.defaultValue(), context.parentSaveHandler()),
|
@Override
|
||||||
this::convertToString,
|
public String getDefault() {
|
||||||
input -> {
|
return convertToString(context.defaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Message> getMessages(String text) {
|
||||||
try {
|
try {
|
||||||
TweedEntryReader<T, ConfigEntry<T>> reader =
|
TweedReadResult<T> readResult = convertFromString(text);
|
||||||
readWriteExtension.getDefinedEntryReader(entry);
|
if (readResult.hasValue() && !readResult.hasIssues()) {
|
||||||
Patchwork readExtData = readWriteExtension.createReadWriteContextExtensionsData();
|
return Collections.emptyList();
|
||||||
//noinspection DataFlowIssue
|
} else if (readResult.hasIssues()) {
|
||||||
return reader.read(
|
Message.Level messageLevel = readResult.hasValue()
|
||||||
new TweedDataReader() {
|
? Message.Level.WARNING
|
||||||
private boolean consumed = false;
|
: Message.Level.ERROR;
|
||||||
|
return Arrays.stream(readResult.issues()).map(issue ->
|
||||||
@Override
|
new Message(messageLevel, literalComponent(issue.exception().getMessage()))
|
||||||
public TweedDataToken peekToken() throws TweedDataReadException {
|
).collect(Collectors.toList());
|
||||||
if (consumed) {
|
} else {
|
||||||
throw new IllegalStateException("Already consumed");
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Collections.singletonList(
|
||||||
|
new Message(Message.Level.ERROR, literalComponent(e.getMessage()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return new TweedDataToken() {
|
|
||||||
@Override
|
|
||||||
public boolean canReadAsString() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String readAsString() {
|
public void save(String s) {
|
||||||
return input;
|
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 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
|
@Override
|
||||||
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||||
return null;
|
return null;
|
||||||
@@ -270,6 +276,57 @@ public class TweedCoatMappersImpl {
|
|||||||
}
|
}
|
||||||
return wrapper[0];
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
|
|||||||
MOD_ID + ".config",
|
MOD_ID + ".config",
|
||||||
GLFW.GLFW_KEY_T,
|
GLFW.GLFW_KEY_T,
|
||||||
//# if MC_VERSION_NUMBER >= 12109
|
//# if MC_VERSION_NUMBER >= 12109
|
||||||
//- KeyMapping.Category.MISC
|
KeyMapping.Category.MISC
|
||||||
//# else
|
//# else
|
||||||
"key.categories.misc"
|
//- "key.categories.misc"
|
||||||
//# end
|
//# end
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -69,9 +69,9 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer {
|
|||||||
|
|
||||||
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
|
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
|
||||||
//# if MC_VERSION_NUMBER >= 12109
|
//# if MC_VERSION_NUMBER >= 12109
|
||||||
//- public ScreenKeyBinding(String name, int key, Category category) {
|
public ScreenKeyBinding(String name, int key, Category category) {
|
||||||
//# else
|
//# else
|
||||||
public ScreenKeyBinding(String name, int key, String category) {
|
//- public ScreenKeyBinding(String name, int key, String category) {
|
||||||
//# end
|
//# end
|
||||||
super(name, key, category);
|
super(name, key, category);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
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.PatchExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
||||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
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.Getter;
|
||||||
import lombok.extern.apachecommons.CommonsLog;
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -84,14 +84,24 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
|
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
|
||||||
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||||
readContextCustomizer.accept(contextExtensionsData);
|
readContextCustomizer.accept(contextExtensionsData);
|
||||||
PatchInfo patchInfo = patchExtension.collectPatchInfo(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) {
|
} catch (Exception e) {
|
||||||
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
||||||
}
|
}
|
||||||
@@ -108,20 +118,54 @@ public class FabricConfigContainerHelper<T extends @Nullable Object> {
|
|||||||
return defaultValueSupplier.get();
|
return defaultValueSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
try (TweedDataReader reader = serde.createReader(new FileInputStream(configFile))) {
|
try (TweedDataReader reader = serde.createReader(Files.newInputStream(configFile.toPath()))) {
|
||||||
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
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) {
|
} catch (Exception e) {
|
||||||
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
log.error("Failed loading config file " + configFile.getAbsolutePath(), e);
|
||||||
return defaultValueSupplier.get();
|
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) {
|
public void writeConfigInConfigDirectory(T configValue) {
|
||||||
File configFile = getConfigFile();
|
File configFile = getConfigFile();
|
||||||
Path tempConfigDirectory = getOrCreateTempConfigDirectory();
|
Path tempConfigDirectory = getOrCreateTempConfigDirectory();
|
||||||
File tempConfigFile = tempConfigDirectory.resolve(getConfigFileName()).toFile();
|
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();
|
Patchwork contextExtensionsData = readWriteExtension.createReadWriteContextExtensionsData();
|
||||||
|
|
||||||
readWriteExtension.write(
|
readWriteExtension.write(
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
package de.siphalor.tweed5.attributesextension.impl.serde.filter;
|
|
||||||
|
|
||||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.jspecify.annotations.NonNull;
|
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AttributesFilteredCompoundEntry<T extends @Nullable Object> implements CompoundConfigEntry<T> {
|
|
||||||
private final CompoundConfigEntry<T> delegate;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(T compoundValue, String key, Object value) {
|
|
||||||
if (value == AttributesReadWriteFilterExtensionImpl.NOOP_MARKER) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delegate.set(compoundValue, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object get(T compoundValue, String key) {
|
|
||||||
return delegate.get(compoundValue, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull T instantiateValue() {
|
|
||||||
return delegate.instantiateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, ConfigEntry<?>> subEntries() {
|
|
||||||
return delegate.subEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigContainer<?> container() {
|
|
||||||
return delegate.container();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> valueClass() {
|
|
||||||
return delegate.valueClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Patchwork extensionsData() {
|
|
||||||
return delegate.extensionsData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
|
||||||
delegate.visitInOrder(visitor, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitInOrder(ConfigEntryVisitor visitor) {
|
|
||||||
delegate.visitInOrder(visitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T deepCopy(T value) {
|
|
||||||
return delegate.deepCopy(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,25 +5,22 @@ import de.siphalor.tweed5.attributesextension.api.AttributesRelatedExtension;
|
|||||||
import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension;
|
import de.siphalor.tweed5.attributesextension.api.serde.filter.AttributesReadWriteFilterExtension;
|
||||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
||||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReadException;
|
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.TweedEntryReader;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
||||||
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.ReadWriteExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||||
import de.siphalor.tweed5.serde.extension.impl.TweedEntryReaderWriterImpls;
|
import de.siphalor.tweed5.serde.extension.impl.TweedEntryReaderWriterImpls;
|
||||||
import de.siphalor.tweed5.serde_api.api.DelegatingTweedDataWriter;
|
import de.siphalor.tweed5.serde_api.api.DelegatingTweedDataWriter;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataUnsupportedValueException;
|
import de.siphalor.tweed5.serde_api.api.TweedDataUnsupportedValueException;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
import de.siphalor.tweed5.serde_api.api.decoration.TweedDataDecoration;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
|
||||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
|
||||||
import de.siphalor.tweed5.utils.api.UniqueSymbol;
|
import de.siphalor.tweed5.utils.api.UniqueSymbol;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -184,13 +181,7 @@ public class AttributesReadWriteFilterExtensionImpl
|
|||||||
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted
|
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted
|
||||||
= (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
= (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||||
|
|
||||||
return new TweedEntryReader<@Nullable Object, ConfigEntry<Object>>() {
|
return (TweedEntryReader<Object, ConfigEntry<Object>>) (reader, entry, context) -> {
|
||||||
@Override
|
|
||||||
public @Nullable Object read(
|
|
||||||
TweedDataReader reader,
|
|
||||||
ConfigEntry<Object> entry,
|
|
||||||
TweedReadContext context
|
|
||||||
) throws TweedEntryReadException {
|
|
||||||
ReadWriteContextCustomData contextData = context.extensionsData().get(readWriteContextDataAccess);
|
ReadWriteContextCustomData contextData = context.extensionsData().get(readWriteContextDataAccess);
|
||||||
if (contextData == null) {
|
if (contextData == null) {
|
||||||
contextData = new ReadWriteContextCustomData();
|
contextData = new ReadWriteContextCustomData();
|
||||||
@@ -198,19 +189,9 @@ public class AttributesReadWriteFilterExtensionImpl
|
|||||||
}
|
}
|
||||||
if (!doFiltersMatch(entry, contextData)) {
|
if (!doFiltersMatch(entry, contextData)) {
|
||||||
TweedEntryReaderWriterImpls.NOOP_READER_WRITER.read(reader, entry, context);
|
TweedEntryReaderWriterImpls.NOOP_READER_WRITER.read(reader, entry, context);
|
||||||
return contextData.noopHandlerInstalled() ? NOOP_MARKER : null;
|
return TweedReadResult.empty();
|
||||||
}
|
|
||||||
if (entry instanceof CompoundConfigEntry) {
|
|
||||||
CompoundConfigEntry<Object> delegated =
|
|
||||||
new AttributesFilteredCompoundEntry<>(((CompoundConfigEntry<Object>) entry));
|
|
||||||
boolean oldNoopHandlerInstalled = contextData.noopHandlerInstalled();
|
|
||||||
contextData.noopHandlerInstalled(true);
|
|
||||||
Object value = innerCasted.read(reader, delegated, context);
|
|
||||||
contextData.noopHandlerInstalled(oldNoopHandlerInstalled);
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
return innerCasted.read(reader, entry, context);
|
return innerCasted.read(reader, entry, context);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
|||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
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.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||||
@@ -164,12 +165,14 @@ class AttributesReadWriteFilterExtensionImplTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")));
|
""")));
|
||||||
Map<String, Object> readValue = configContainer.rootEntry().call(read(
|
TweedReadResult<Map<String, Object>> readResult = configContainer.rootEntry().call(read(
|
||||||
reader,
|
reader,
|
||||||
patchwork -> attributesReadWriteFilterExtension.addFilter(patchwork, "type", "a")
|
patchwork -> attributesReadWriteFilterExtension.addFilter(patchwork, "type", "a")
|
||||||
));
|
));
|
||||||
|
|
||||||
assertThat(readValue)
|
assertThat(readResult)
|
||||||
|
.extracting(TweedReadResult::value)
|
||||||
|
.asInstanceOf(map(String.class, Object.class))
|
||||||
.containsEntry("first", "1st")
|
.containsEntry("first", "1st")
|
||||||
.doesNotContainKey("second")
|
.doesNotContainKey("second")
|
||||||
.hasEntrySatisfying(
|
.hasEntrySatisfying(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
|||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
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.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||||
@@ -25,6 +26,7 @@ import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderW
|
|||||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.InstanceOfAssertFactories.map;
|
||||||
|
|
||||||
public class AttributesReadWriteFilterExtensionImplWithPatchInfoTest {
|
public class AttributesReadWriteFilterExtensionImplWithPatchInfoTest {
|
||||||
@Test
|
@Test
|
||||||
@@ -65,7 +67,7 @@ public class AttributesReadWriteFilterExtensionImplWithPatchInfoTest {
|
|||||||
var patchInfo = patchExtension.collectPatchInfo(readExtData);
|
var patchInfo = patchExtension.collectPatchInfo(readExtData);
|
||||||
filterExtension.addFilter(readExtData, "type", "a");
|
filterExtension.addFilter(readExtData, "type", "a");
|
||||||
|
|
||||||
var value = readWriteExtension.read(
|
var readResult = readWriteExtension.read(
|
||||||
new HjsonReader(new HjsonLexer(new StringReader("""
|
new HjsonReader(new HjsonLexer(new StringReader("""
|
||||||
{
|
{
|
||||||
"first": "FIRST",
|
"first": "FIRST",
|
||||||
@@ -76,8 +78,10 @@ public class AttributesReadWriteFilterExtensionImplWithPatchInfoTest {
|
|||||||
readExtData
|
readExtData
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(value).extracting(v -> v.get("first")).isEqualTo("FIRST");
|
assertThat(readResult)
|
||||||
assertThat(value).extracting(v -> v.get("second")).isNull();
|
.extracting(TweedReadResult::value)
|
||||||
|
.asInstanceOf(map(String.class, Object.class))
|
||||||
|
.isEqualTo(Map.of("first", "FIRST"));
|
||||||
assertThat(patchInfo.containsEntry(firstEntry)).isTrue();
|
assertThat(patchInfo.containsEntry(firstEntry)).isTrue();
|
||||||
assertThat(patchInfo.containsEntry(secondEntry)).isFalse();
|
assertThat(patchInfo.containsEntry(secondEntry)).isFalse();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ package de.siphalor.tweed5.defaultextensions.patch.impl;
|
|||||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReadException;
|
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
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.ReadWriteExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
import de.siphalor.tweed5.defaultextensions.patch.api.PatchInfo;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||||
@@ -96,20 +94,13 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted =
|
TweedEntryReader<Object, ConfigEntry<Object>> innerCasted =
|
||||||
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||||
return new TweedEntryReader<@Nullable Object, ConfigEntry<Object>>() {
|
return (TweedEntryReader<Object, ConfigEntry<Object>>) (reader, entry, context) -> {
|
||||||
@Override
|
TweedReadResult<Object> readResult = innerCasted.read(reader, entry, context);
|
||||||
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);
|
ReadWriteContextCustomData customData = context.extensionsData().get(readWriteContextDataAccess);
|
||||||
if (customData != null && customData.patchInfo() != null) {
|
if (customData != null && customData.patchInfo() != null) {
|
||||||
customData.patchInfo().addEntry(entry);
|
customData.patchInfo().addEntry(entry);
|
||||||
}
|
}
|
||||||
return readValue;
|
return readResult;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,21 +70,7 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
|
|
||||||
pathTracking = PathTracking.create();
|
pathTracking = PathTracking.create();
|
||||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||||
try {
|
|
||||||
return castedInner.read(new PathTrackingDataReader(reader, pathTracking), entry, context);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -116,7 +102,8 @@ public class PatherExtensionImpl implements PatherExtension, ReadWriteRelatedExt
|
|||||||
try {
|
try {
|
||||||
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
||||||
} catch (TweedEntryWriteException e) {
|
} catch (TweedEntryWriteException e) {
|
||||||
val exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
|
PathTracking exceptionPathTracking =
|
||||||
|
e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||||
if (exceptionPathTracking != null) {
|
if (exceptionPathTracking != null) {
|
||||||
throw new TweedEntryWriteException(
|
throw new TweedEntryWriteException(
|
||||||
"Exception while writing entry at "
|
"Exception while writing entry at "
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package de.siphalor.tweed5.defaultextensions.readfallback.impl;
|
|||||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReadException;
|
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReader;
|
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.ReadWriteExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
||||||
@@ -11,6 +10,7 @@ import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
|||||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension;
|
import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||||
import lombok.extern.apachecommons.CommonsLog;
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
|
|
||||||
@@ -30,8 +30,6 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
|
|||||||
PresetsExtension presetsExtension = configContainer.extension(PresetsExtension.class)
|
PresetsExtension presetsExtension = configContainer.extension(PresetsExtension.class)
|
||||||
.orElseThrow(() -> new IllegalStateException(getClass().getSimpleName()
|
.orElseThrow(() -> new IllegalStateException(getClass().getSimpleName()
|
||||||
+ " requires " + ReadFallbackExtension.class.getSimpleName()));
|
+ " requires " + ReadFallbackExtension.class.getSimpleName()));
|
||||||
PatherExtension patherExtension = configContainer.extension(PatherExtension.class).orElse(null);
|
|
||||||
|
|
||||||
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>>() {
|
context.registerReaderMiddleware(new Middleware<TweedEntryReader<?, ?>>() {
|
||||||
@Override
|
@Override
|
||||||
public String id() {
|
public String id() {
|
||||||
@@ -53,23 +51,16 @@ public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWri
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
TweedEntryReader<Object, ConfigEntry<Object>> castedInner =
|
TweedEntryReader<Object, ConfigEntry<Object>> castedInner =
|
||||||
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
(TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) inner;
|
||||||
return (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) (reader, entry, context) -> {
|
return (TweedEntryReader<Object, @NonNull ConfigEntry<Object>>) (reader, entry, context) ->
|
||||||
try {
|
castedInner.read(reader, entry, context).catchError(
|
||||||
return castedInner.read(reader, entry, context);
|
issues -> {
|
||||||
} catch (TweedEntryReadException e) {
|
Object fallback =
|
||||||
if (patherExtension == null) {
|
presetsExtension.presetValue(entry, PresetsExtension.DEFAULT_PRESET_NAME);
|
||||||
log.error("Failed to read entry: " + e.getMessage(), e);
|
return TweedReadResult.withIssues(fallback, issues);
|
||||||
} else {
|
},
|
||||||
log.error(
|
context
|
||||||
"Failed to read entry: " + e.getMessage()
|
|
||||||
+ " at " + patherExtension.getPath(context),
|
|
||||||
e
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return presetsExtension.presetValue(entry, PresetsExtension.DEFAULT_PRESET_NAME);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
|||||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||||
import de.siphalor.tweed5.core.api.middleware.MiddlewareContainer;
|
import de.siphalor.tweed5.core.api.middleware.MiddlewareContainer;
|
||||||
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.extension.ReadWriteExtensionSetupContext;
|
|
||||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteRelatedExtension;
|
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
|
||||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
import de.siphalor.tweed5.defaultextensions.comment.api.CommentModifyingExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
import de.siphalor.tweed5.defaultextensions.comment.api.CommentProducer;
|
||||||
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
|
||||||
@@ -30,6 +24,13 @@ import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssu
|
|||||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
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.read.result.TweedReadIssue;
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||||
|
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
@@ -233,8 +234,7 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
|||||||
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
|
return (TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) -> {
|
||||||
ValidationIssues validationIssues = getOrCreateValidationIssues(context.extensionsData());
|
ValidationIssues validationIssues = getOrCreateValidationIssues(context.extensionsData());
|
||||||
|
|
||||||
Object value = castedInner.read(reader, entry, context);
|
return castedInner.read(reader, entry, context).andThen(value -> {
|
||||||
|
|
||||||
ConfigEntryValidator entryValidator = entry.extensionsData()
|
ConfigEntryValidator entryValidator = entry.extensionsData()
|
||||||
.get(customEntryDataAccess)
|
.get(customEntryDataAccess)
|
||||||
.completeValidator();
|
.completeValidator();
|
||||||
@@ -242,26 +242,39 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
|||||||
|
|
||||||
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
|
ValidationResult<Object> validationResult = entryValidator.validate(entry, value);
|
||||||
|
|
||||||
if (!validationResult.issues().isEmpty()) {
|
if (validationResult.issues().isEmpty()) {
|
||||||
|
return TweedReadResult.ok(validationResult.value());
|
||||||
|
}
|
||||||
|
|
||||||
String path = patherExtension.getPath(context);
|
String path = patherExtension.getPath(context);
|
||||||
validationIssues.issuesByPath().put(path, new ValidationIssues.EntryIssues(
|
validationIssues.issuesByPath().put(path, new ValidationIssues.EntryIssues(
|
||||||
entry,
|
entry,
|
||||||
validationResult.issues()
|
validationResult.issues()
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
if (validationResult.hasError()) {
|
if (validationResult.hasError()) {
|
||||||
throw new TweedEntryReadException(
|
return TweedReadResult.failed(
|
||||||
"Failed to validate entry: " + validationResult.issues(),
|
mapValidationIssuesToReadIssues(validationResult.issues(), context)
|
||||||
context
|
);
|
||||||
|
} else {
|
||||||
|
return TweedReadResult.withIssues(
|
||||||
|
validationResult.value(),
|
||||||
|
mapValidationIssuesToReadIssues(validationResult.issues(), context)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}, 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) {
|
private ValidationIssues getOrCreateValidationIssues(Patchwork readContextExtensionsData) {
|
||||||
assert readContextValidationIssuesAccess != null;
|
assert readContextValidationIssuesAccess != null;
|
||||||
ValidationIssues validationIssues = readContextExtensionsData.get(readContextValidationIssuesAccess);
|
ValidationIssues validationIssues = readContextExtensionsData.get(readContextValidationIssuesAccess);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl;
|
|||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
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.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
import de.siphalor.tweed5.defaultextensions.patch.api.PatchExtension;
|
||||||
@@ -150,7 +151,7 @@ class PatchExtensionImplTest {
|
|||||||
PatchExtension patchExtension = configContainer.extension(PatchExtension.class).orElseThrow();
|
PatchExtension patchExtension = configContainer.extension(PatchExtension.class).orElseThrow();
|
||||||
|
|
||||||
var patchInfo = new AtomicReference<@Nullable PatchInfo>();
|
var patchInfo = new AtomicReference<@Nullable PatchInfo>();
|
||||||
Map<String, Object> patchValue = rootEntry.call(read(
|
TweedReadResult<Map<String, Object>> patchResult = rootEntry.call(read(
|
||||||
reader, extensionsData ->
|
reader, extensionsData ->
|
||||||
patchInfo.set(patchExtension.collectPatchInfo(extensionsData))
|
patchInfo.set(patchExtension.collectPatchInfo(extensionsData))
|
||||||
));
|
));
|
||||||
@@ -158,7 +159,7 @@ class PatchExtensionImplTest {
|
|||||||
Map<String, Object> resultValue = patchExtension.patch(
|
Map<String, Object> resultValue = patchExtension.patch(
|
||||||
rootEntry,
|
rootEntry,
|
||||||
baseValue,
|
baseValue,
|
||||||
patchValue,
|
patchResult.value(),
|
||||||
Objects.requireNonNull(patchInfo.get())
|
Objects.requireNonNull(patchInfo.get())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
package de.siphalor.tweed5.defaultextensions.readfallback.impl;
|
package de.siphalor.tweed5.defaultextensions.readfallback.impl;
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
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.CompoundConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||||
|
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.ReadWriteExtension;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryReadException;
|
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
|
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.TweedEntryReaderWriter;
|
||||||
import de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters;
|
import de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||||
@@ -19,9 +22,6 @@ import de.siphalor.tweed5.serde_api.api.TweedDataReadException;
|
|||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriteException;
|
import de.siphalor.tweed5.serde_api.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.testutils.generic.log.LogCaptureMockitoExtension;
|
import de.siphalor.tweed5.testutils.generic.log.LogCaptureMockitoExtension;
|
||||||
import de.siphalor.tweed5.testutils.generic.log.LogsCaptor;
|
import de.siphalor.tweed5.testutils.generic.log.LogsCaptor;
|
||||||
import org.jspecify.annotations.NullMarked;
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -34,12 +34,11 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.entryReaderWriter;
|
||||||
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.read;
|
import static de.siphalor.tweed5.serde.extension.api.ReadWriteExtension.read;
|
||||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.compoundReaderWriter;
|
||||||
import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.presetValue;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
|
|
||||||
|
|
||||||
@ExtendWith(LogCaptureMockitoExtension.class)
|
@ExtendWith(LogCaptureMockitoExtension.class)
|
||||||
@NullMarked
|
@NullMarked
|
||||||
@@ -59,12 +58,18 @@ class ReadFallbackExtensionImplTest {
|
|||||||
configContainer.attachTree(entry);
|
configContainer.attachTree(entry);
|
||||||
configContainer.initialize();
|
configContainer.initialize();
|
||||||
|
|
||||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12")))))).isEqualTo(12);
|
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12"))))))
|
||||||
|
.isEqualTo(TweedReadResult.ok(12));
|
||||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
|
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
|
||||||
logsCaptor.clear();
|
logsCaptor.clear();
|
||||||
|
|
||||||
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).isEqualTo(-1);
|
assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).satisfies(
|
||||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).hasSize(1);
|
r -> assertThat(r).extracting(TweedReadResult::value).isEqualTo(-1),
|
||||||
|
r -> assertThat(r.issues()).singleElement()
|
||||||
|
.extracting(TweedReadIssue::exception)
|
||||||
|
.extracting(Throwable::getMessage)
|
||||||
|
.isEqualTo("Value is not even")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
@@ -105,6 +110,7 @@ class ReadFallbackExtensionImplTest {
|
|||||||
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
||||||
"{first: {second: 12}}"
|
"{first: {second: 12}}"
|
||||||
))))))
|
))))))
|
||||||
|
.extracting(TweedReadResult::value)
|
||||||
.extracting(map -> (Map<String, Object>) map.get("first"))
|
.extracting(map -> (Map<String, Object>) map.get("first"))
|
||||||
.extracting(map -> (Integer) map.get("second"))
|
.extracting(map -> (Integer) map.get("second"))
|
||||||
.isEqualTo(12);
|
.isEqualTo(12);
|
||||||
@@ -114,22 +120,20 @@ class ReadFallbackExtensionImplTest {
|
|||||||
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
assertThat(root.call(read(new HjsonReader(new HjsonLexer(new StringReader(
|
||||||
"{first: {second: 13}}"
|
"{first: {second: 13}}"
|
||||||
))))))
|
))))))
|
||||||
|
.extracting(TweedReadResult::value)
|
||||||
.extracting(map -> (Map<String, Object>) map.get("first"))
|
.extracting(map -> (Map<String, Object>) map.get("first"))
|
||||||
.extracting(map -> (Integer) map.get("second"))
|
.extracting(map -> (Integer) map.get("second"))
|
||||||
.isEqualTo(-1);
|
.isEqualTo(-1);
|
||||||
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).singleElement()
|
assertThat(logsCaptor.getLogsForLevel(Level.ERROR)).isEmpty();
|
||||||
.extracting(ILoggingEvent::getMessage)
|
|
||||||
.asInstanceOf(STRING)
|
|
||||||
.contains("first.second");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class EvenIntReader implements TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> {
|
private static class EvenIntReader implements TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> {
|
||||||
@Override
|
@Override
|
||||||
public Integer read(
|
public TweedReadResult<Integer> read(
|
||||||
TweedDataReader reader,
|
TweedDataReader reader,
|
||||||
ConfigEntry<Integer> entry,
|
ConfigEntry<Integer> entry,
|
||||||
TweedReadContext context
|
TweedReadContext context
|
||||||
) throws TweedEntryReadException {
|
) {
|
||||||
int value;
|
int value;
|
||||||
try {
|
try {
|
||||||
value = reader.readToken().readAsInt();
|
value = reader.readToken().readAsInt();
|
||||||
@@ -137,9 +141,9 @@ class ReadFallbackExtensionImplTest {
|
|||||||
throw new IllegalStateException("Should not be called", e);
|
throw new IllegalStateException("Should not be called", e);
|
||||||
}
|
}
|
||||||
if (value % 2 == 0) {
|
if (value % 2 == 0) {
|
||||||
return value;
|
return TweedReadResult.ok(value);
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException("Value is not even", context);
|
return TweedReadResult.error(TweedReadIssue.error("Value is not even", context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
|||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
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.HjsonCommentType;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
@@ -156,12 +157,14 @@ class ValidationExtensionImplTest {
|
|||||||
}
|
}
|
||||||
""")));
|
""")));
|
||||||
var validationIssues = new AtomicReference<@Nullable ValidationIssues>();
|
var validationIssues = new AtomicReference<@Nullable ValidationIssues>();
|
||||||
Map<String, Object> value = configContainer.rootEntry().call(read(
|
TweedReadResult<Map<String, Object>> value = configContainer.rootEntry().call(read(
|
||||||
reader,
|
reader,
|
||||||
extensionsData -> validationIssues.set(validationExtension.captureValidationIssues(extensionsData))
|
extensionsData -> validationIssues.set(validationExtension.captureValidationIssues(extensionsData))
|
||||||
));
|
));
|
||||||
|
|
||||||
assertThat(value).isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
|
assertThat(value)
|
||||||
|
.extracting(TweedReadResult::value)
|
||||||
|
.isEqualTo(Map.of("byte", (byte) 11, "int", 123, "double", 0.5));
|
||||||
//noinspection DataFlowIssue
|
//noinspection DataFlowIssue
|
||||||
assertThat(validationIssues.get()).isNotNull().satisfies(
|
assertThat(validationIssues.get()).isNotNull().satisfies(
|
||||||
vi -> assertValidationIssue(
|
vi -> assertValidationIssue(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
|||||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||||
import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
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.HjsonCommentType;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
@@ -30,6 +31,7 @@ import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.
|
|||||||
import static de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension.validators;
|
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.ReadWriteExtension.*;
|
||||||
import static de.siphalor.tweed5.serde.extension.api.readwrite.TweedEntryReaderWriters.*;
|
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;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
class ValidationFallbackExtensionImplTest {
|
class ValidationFallbackExtensionImplTest {
|
||||||
@@ -114,8 +116,9 @@ class ValidationFallbackExtensionImplTest {
|
|||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(strings = {"0", "7", "123", "null"})
|
@ValueSource(strings = {"0", "7", "123", "null"})
|
||||||
void fallbackTriggers(String input) {
|
void fallbackTriggers(String input) {
|
||||||
Integer result = intEntry.call(read(new HjsonReader(new HjsonLexer(new StringReader(input)))));
|
TweedReadResult<Integer> result = intEntry.call(read(new HjsonReader(new HjsonLexer(new StringReader(input)))));
|
||||||
assertEquals(3, result);
|
assertThat(result).extracting(TweedReadResult::value).isEqualTo(3);
|
||||||
|
assertThat(result.issues()).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package de.siphalor.tweed5.serde.extension.api;
|
|||||||
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||||
|
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.TweedEntryReaderWriter;
|
||||||
import de.siphalor.tweed5.serde.extension.impl.ReadWriteExtensionImpl;
|
import de.siphalor.tweed5.serde.extension.impl.ReadWriteExtensionImpl;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
import de.siphalor.tweed5.serde_api.api.TweedDataReader;
|
||||||
@@ -55,11 +56,11 @@ public interface ReadWriteExtension extends TweedExtension {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T extends @Nullable Object> Function<ConfigEntry<T>, T> read(TweedDataReader reader) {
|
static <T extends @Nullable Object> Function<ConfigEntry<T>, TweedReadResult<T>> read(TweedDataReader reader) {
|
||||||
return read(reader, null);
|
return read(reader, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T extends @Nullable Object> Function<ConfigEntry<T>, T> read(
|
static <T extends @Nullable Object> Function<ConfigEntry<T>, TweedReadResult<T>> read(
|
||||||
TweedDataReader reader,
|
TweedDataReader reader,
|
||||||
@Nullable Consumer<Patchwork> contextExtensionsDataCustomizer
|
@Nullable Consumer<Patchwork> contextExtensionsDataCustomizer
|
||||||
) {
|
) {
|
||||||
@@ -115,7 +116,7 @@ public interface ReadWriteExtension extends TweedExtension {
|
|||||||
|
|
||||||
Patchwork createReadWriteContextExtensionsData();
|
Patchwork createReadWriteContextExtensionsData();
|
||||||
|
|
||||||
<T extends @Nullable Object> T read(TweedDataReader reader, ConfigEntry<T> entry, Patchwork contextExtensionsData)
|
<T extends @Nullable Object> TweedReadResult<T> read(TweedDataReader reader, ConfigEntry<T> entry, Patchwork contextExtensionsData)
|
||||||
throws TweedEntryReadException;
|
throws TweedEntryReadException;
|
||||||
|
|
||||||
<T extends @Nullable Object> void write(
|
<T extends @Nullable Object> void write(
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package de.siphalor.tweed5.serde.extension.api;
|
package de.siphalor.tweed5.serde.extension.api;
|
||||||
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
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.TweedDataReader;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface TweedEntryReader<T extends @Nullable Object, C extends ConfigEntry<T>> {
|
public interface TweedEntryReader<T extends @Nullable Object, C extends ConfigEntry<T>> {
|
||||||
T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException;
|
TweedReadResult<T> read(TweedDataReader reader, C entry, TweedReadContext context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.siphalor.tweed5.serde.extension.api.read.result;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface ThrowingFunction<T extends @Nullable Object, R extends @Nullable Object> {
|
||||||
|
R apply(T value) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package de.siphalor.tweed5.serde.extension.api.read.result;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThrowingReadFunction<T extends @Nullable Object, R extends @Nullable Object> {
|
||||||
|
static <T extends @Nullable Object, R extends @Nullable Object> ThrowingReadFunction<T, R> any(
|
||||||
|
TweedReadContext readContext,
|
||||||
|
ThrowingFunction<T, R>... functions
|
||||||
|
) {
|
||||||
|
//noinspection unchecked
|
||||||
|
ThrowingReadFunction<T, R>[] wrappedFunctions = new ThrowingReadFunction[functions.length];
|
||||||
|
for (int i = 0; i < functions.length; i++) {
|
||||||
|
wrappedFunctions[i] = wrap(readContext, functions[i]);
|
||||||
|
}
|
||||||
|
return any(wrappedFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T extends @Nullable Object, R extends @Nullable Object> ThrowingReadFunction<T, R> any(
|
||||||
|
ThrowingReadFunction<T, R>... functions
|
||||||
|
) {
|
||||||
|
if (functions.length == 0) {
|
||||||
|
throw new IllegalArgumentException("At least one function is required");
|
||||||
|
}
|
||||||
|
return value -> {
|
||||||
|
List<TweedReadIssue> issues = null;
|
||||||
|
boolean foundEmpty = false;
|
||||||
|
for (ThrowingReadFunction<T, R> function : functions) {
|
||||||
|
TweedReadResult<R> result = function.apply(value);
|
||||||
|
if (result.hasValue()) {
|
||||||
|
return result;
|
||||||
|
} else if (!result.isFailed()) {
|
||||||
|
foundEmpty = true;
|
||||||
|
}
|
||||||
|
if (result.hasIssues()) {
|
||||||
|
if (issues == null) {
|
||||||
|
issues = new ArrayList<>(functions.length);
|
||||||
|
issues.addAll(Arrays.asList(result.issues()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foundEmpty) {
|
||||||
|
return TweedReadResult.empty();
|
||||||
|
}
|
||||||
|
if (issues != null) {
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Unreachable");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T extends @Nullable Object, R extends @Nullable Object> ThrowingReadFunction<T, R> wrap(
|
||||||
|
TweedReadContext readContext,
|
||||||
|
ThrowingFunction<T, R> function
|
||||||
|
) {
|
||||||
|
return value -> {
|
||||||
|
try {
|
||||||
|
return TweedReadResult.ok(function.apply(value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return TweedReadResult.failed(TweedReadIssue.error(e, readContext));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TweedReadResult<R> apply(T value) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package de.siphalor.tweed5.serde.extension.api.read.result;
|
||||||
|
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class TweedReadIssue {
|
||||||
|
@Getter
|
||||||
|
private final Exception exception;
|
||||||
|
private final TweedReadContext readContext;
|
||||||
|
|
||||||
|
public static TweedReadIssue error(String message, TweedReadContext readContext) {
|
||||||
|
return new TweedReadIssue(new Exception(message), readContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedReadIssue error(Exception exception, TweedReadContext readContext) {
|
||||||
|
return new TweedReadIssue(exception, readContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TweedReadIssue(" + exception + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package de.siphalor.tweed5.serde.extension.api.read.result;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.TweedReadContext;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
@EqualsAndHashCode(doNotUseGetters = true)
|
||||||
|
public class TweedReadResult<T extends @Nullable Object> {
|
||||||
|
private static final TweedReadResult<?> EMPTY = new TweedReadResult<>(null, false, null, Severity.OK);
|
||||||
|
|
||||||
|
private final T value;
|
||||||
|
private final boolean hasValue;
|
||||||
|
private final TweedReadIssue @Nullable [] issues;
|
||||||
|
private final Severity severity;
|
||||||
|
|
||||||
|
public static <T extends @Nullable Object> TweedReadResult<T> ok(T value) {
|
||||||
|
return new TweedReadResult<>(value, true, null, Severity.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends @Nullable Object> TweedReadResult<T> empty() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (TweedReadResult<T>) EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends @Nullable Object> TweedReadResult<T> withIssues(T value, TweedReadIssue... issues) {
|
||||||
|
return new TweedReadResult<>(value, true, issues, Severity.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends @Nullable Object> TweedReadResult<T> error(TweedReadIssue... issues) {
|
||||||
|
return new TweedReadResult<>(null, false, issues, Severity.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends @Nullable Object> TweedReadResult<T> failed(TweedReadIssue... issues) {
|
||||||
|
if (issues.length == 0) {
|
||||||
|
throw new IllegalArgumentException("At least one issue is required for a failed read result");
|
||||||
|
}
|
||||||
|
return new TweedReadResult<>(null, false, issues, Severity.FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T value() {
|
||||||
|
if (hasValue) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("No value present");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue() {
|
||||||
|
return hasValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasIssues() {
|
||||||
|
return issues != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweedReadIssue [] issues() {
|
||||||
|
if (issues == null) {
|
||||||
|
return new TweedReadIssue[0];
|
||||||
|
}
|
||||||
|
return issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isError() {
|
||||||
|
return severity == Severity.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFailed() {
|
||||||
|
return severity == Severity.FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R extends @Nullable Object> TweedReadResult<R> map(
|
||||||
|
ThrowingFunction<T, R> function,
|
||||||
|
TweedReadContext readContext
|
||||||
|
) {
|
||||||
|
return andThen(value -> ok(function.apply(value)), readContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R extends @Nullable Object> TweedReadResult<R> andThen(
|
||||||
|
ThrowingReadFunction<T, R> function,
|
||||||
|
TweedReadContext readContext
|
||||||
|
) {
|
||||||
|
if (severity != Severity.OK || !hasValue()) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (TweedReadResult<R>) this;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
TweedReadResult<R> innerResult = function.apply(value());
|
||||||
|
TweedReadIssue[] issues = combineIssues(issues(), innerResult.issues());
|
||||||
|
if (issues.length == 0) {
|
||||||
|
return innerResult;
|
||||||
|
}
|
||||||
|
if (innerResult.isFailed()) {
|
||||||
|
return failed(issues);
|
||||||
|
} else if (innerResult.isError()) {
|
||||||
|
return error(issues);
|
||||||
|
} else if (innerResult.hasValue()) {
|
||||||
|
return withIssues(innerResult.value(), issues);
|
||||||
|
} else {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
if (hasIssues()) {
|
||||||
|
TweedReadIssue[] issues = new TweedReadIssue[issues().length + 1];
|
||||||
|
System.arraycopy(issues(), 0, issues, 0, issues().length);
|
||||||
|
issues[issues().length] = TweedReadIssue.error(exception, readContext);
|
||||||
|
return failed(issues);
|
||||||
|
}
|
||||||
|
return failed(TweedReadIssue.error(exception, readContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweedReadResult<T> catchError(
|
||||||
|
ThrowingFunction<TweedReadIssue[], TweedReadResult<T>> errorHandler,
|
||||||
|
TweedReadContext readContext
|
||||||
|
) {
|
||||||
|
if (severity != Severity.ERROR) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return errorHandler.apply(issues());
|
||||||
|
} catch (Exception exception) {
|
||||||
|
TweedReadIssue[] issues = new TweedReadIssue[issues().length + 1];
|
||||||
|
System.arraycopy(issues(), 0, issues, 0, issues().length);
|
||||||
|
issues[issues().length] = TweedReadIssue.error(exception, readContext);
|
||||||
|
return error(issues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TweedReadIssue[] combineIssues(TweedReadIssue[] a, TweedReadIssue[] b) {
|
||||||
|
if (a.length == 0) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b.length == 0) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
TweedReadIssue[] result = new TweedReadIssue[a.length + b.length];
|
||||||
|
System.arraycopy(a, 0, result, 0, a.length);
|
||||||
|
System.arraycopy(b, 0, result, a.length, b.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (severity == Severity.FAILURE) {
|
||||||
|
return "TweedReadResult#failed(issues=" + Arrays.toString(issues) + ')';
|
||||||
|
} else if (severity == Severity.ERROR) {
|
||||||
|
return "TweedReadResult#error(issues=" + Arrays.toString(issues) + ')';
|
||||||
|
} else if (hasValue) {
|
||||||
|
if (hasIssues()) {
|
||||||
|
return "TweedReadResult#withIssues(value=" + value + ", issues=" + Arrays.toString(issues) + ')';
|
||||||
|
} else {
|
||||||
|
return "TweedReadResult#ok(value=" + value + ')';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "TweedReadResult#empty";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Severity {
|
||||||
|
OK,
|
||||||
|
ERROR,
|
||||||
|
FAILURE,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@NullMarked
|
||||||
|
package de.siphalor.tweed5.serde.extension.api.read.result;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -9,6 +9,7 @@ import de.siphalor.tweed5.core.api.middleware.Middleware;
|
|||||||
import de.siphalor.tweed5.serde.extension.api.*;
|
import de.siphalor.tweed5.serde.extension.api.*;
|
||||||
import de.siphalor.tweed5.serde.extension.api.extension.ReadWriteExtensionSetupContext;
|
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.ReadWriteRelatedExtension;
|
||||||
|
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.TweedDataReader;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
import de.siphalor.tweed5.serde_api.api.TweedDataVisitor;
|
||||||
import de.siphalor.tweed5.serde_api.api.TweedDataWriteException;
|
import de.siphalor.tweed5.serde_api.api.TweedDataWriteException;
|
||||||
@@ -136,7 +137,7 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
|||||||
return readWriteContextPatchworkFactory.create();
|
return readWriteContextPatchworkFactory.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends @Nullable Object> T read(
|
public <T extends @Nullable Object> TweedReadResult<T> read(
|
||||||
TweedDataReader reader,
|
TweedDataReader reader,
|
||||||
ConfigEntry<T> entry,
|
ConfigEntry<T> entry,
|
||||||
Patchwork contextExtensionsData
|
Patchwork contextExtensionsData
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
|||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.entry.NullableConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.NullableConfigEntry;
|
||||||
import de.siphalor.tweed5.serde.extension.api.*;
|
import de.siphalor.tweed5.serde.extension.api.*;
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.read.result.ThrowingReadFunction;
|
||||||
|
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.TweedEntryReaderWriter;
|
||||||
import de.siphalor.tweed5.serde_api.api.*;
|
import de.siphalor.tweed5.serde_api.api.*;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
@@ -38,15 +41,14 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
public static class NullableReaderWriter<T extends @Nullable Object> implements TweedEntryReaderWriter<T, NullableConfigEntry<T>> {
|
public static class NullableReaderWriter<T extends @Nullable Object> implements TweedEntryReaderWriter<T, NullableConfigEntry<T>> {
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, NullableConfigEntry<T> entry, TweedReadContext context)
|
public TweedReadResult<T> read(TweedDataReader reader, NullableConfigEntry<T> entry, TweedReadContext context) {
|
||||||
throws TweedEntryReadException {
|
|
||||||
try {
|
try {
|
||||||
if (reader.peekToken().isNull()) {
|
if (reader.peekToken().isNull()) {
|
||||||
reader.readToken();
|
reader.readToken();
|
||||||
return null;
|
return TweedReadResult.ok(null);
|
||||||
}
|
}
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException(e, context);
|
return TweedReadResult.failed(TweedReadIssue.error(e, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
TweedEntryReader<T, ConfigEntry<T>> nonNullReader = context.readWriteExtension().getReaderChain(entry.nonNullEntry());
|
TweedEntryReader<T, ConfigEntry<T>> nonNullReader = context.readWriteExtension().getReaderChain(entry.nonNullEntry());
|
||||||
@@ -75,14 +77,14 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
private final TweedEntryReader<T, C> delegate;
|
private final TweedEntryReader<T, C> delegate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException {
|
public TweedReadResult<T> read(TweedDataReader reader, C entry, TweedReadContext context) {
|
||||||
try {
|
try {
|
||||||
if (reader.peekToken().isNull()) {
|
if (reader.peekToken().isNull()) {
|
||||||
reader.readToken();
|
reader.readToken();
|
||||||
return null;
|
return TweedReadResult.ok(null);
|
||||||
}
|
}
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException(e, context);
|
return TweedReadResult.failed(TweedReadIssue.error(e, context));
|
||||||
}
|
}
|
||||||
return delegate.read(reader, entry, context);
|
return delegate.read(reader, entry, context);
|
||||||
}
|
}
|
||||||
@@ -109,12 +111,11 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
private final PrimitiveWriteFunction<T> writerFunction;
|
private final PrimitiveWriteFunction<T> writerFunction;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context)
|
public TweedReadResult<T> read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) {
|
||||||
throws TweedEntryReadException {
|
|
||||||
try {
|
try {
|
||||||
return readerFunction.read(reader.readToken());
|
return TweedReadResult.ok(readerFunction.read(reader.readToken()));
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException(e, context);
|
return TweedReadResult.failed(TweedReadIssue.error(e, context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,20 +129,15 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public static class EnumReaderWriter<T extends Enum<?>> implements TweedEntryReaderWriter<T, ConfigEntry<T>> {
|
public static class EnumReaderWriter<T extends Enum<?>> implements TweedEntryReaderWriter<T, ConfigEntry<T>> {
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) throws
|
public TweedReadResult<T> read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) {
|
||||||
TweedEntryReadException {
|
|
||||||
try {
|
|
||||||
TweedDataToken token = reader.readToken();
|
|
||||||
assertIsToken(token, TweedDataToken::canReadAsString, "Expected string", context);
|
|
||||||
//noinspection unchecked,rawtypes
|
//noinspection unchecked,rawtypes
|
||||||
return (T) Enum.valueOf(((Class) entry.valueClass()), token.readAsString());
|
return requireToken(reader, TweedDataToken::canReadAsString, "Expected string", context)
|
||||||
} catch (TweedDataReadException | IllegalArgumentException e) {
|
.map(TweedDataToken::readAsString, context)
|
||||||
throw new TweedEntryReadException(
|
.andThen(ThrowingReadFunction.any(
|
||||||
"Failed reading enum value for " + entry.valueClass().getName(),
|
context,
|
||||||
e,
|
value -> (T) Enum.valueOf(((Class) entry.valueClass()), value),
|
||||||
context
|
value -> (T) Enum.valueOf(((Class) entry.valueClass()), value.toUpperCase(Locale.ROOT))
|
||||||
);
|
), context);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -158,49 +154,46 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
public static class CollectionReaderWriter<T extends @Nullable Object, C extends Collection<T>> implements TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> {
|
public static class CollectionReaderWriter<T extends @Nullable Object, C extends Collection<T>> implements TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> {
|
||||||
@Override
|
@Override
|
||||||
public C read(TweedDataReader reader, CollectionConfigEntry<T, C> entry, TweedReadContext context) throws
|
public TweedReadResult<C> read(TweedDataReader reader, CollectionConfigEntry<T, C> entry, TweedReadContext context) {
|
||||||
TweedEntryReadException {
|
return requireToken(reader, TweedDataToken::isListStart, "Expected list start", context).andThen(_token -> {
|
||||||
TweedDataToken token;
|
if (reader.peekToken().isListEnd()) {
|
||||||
try {
|
return TweedReadResult.ok(entry.instantiateCollection(0));
|
||||||
assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start", context);
|
|
||||||
token = reader.peekToken();
|
|
||||||
} catch (TweedDataReadException e) {
|
|
||||||
throw new TweedEntryReadException("Failed reading collection start", e, context);
|
|
||||||
}
|
|
||||||
if (token.isListEnd()) {
|
|
||||||
return entry.instantiateCollection(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigEntry<T> elementEntry = entry.elementEntry();
|
ConfigEntry<T> elementEntry = entry.elementEntry();
|
||||||
TweedEntryReader<T, ConfigEntry<T>> elementReader = context.readWriteExtension().getReaderChain(elementEntry);
|
TweedEntryReader<T, ConfigEntry<T>> elementReader = context.readWriteExtension().getReaderChain(elementEntry);
|
||||||
|
|
||||||
List<@Nullable T> list = new ArrayList<>(20);
|
List<T> list = new ArrayList<>(20);
|
||||||
|
List<TweedReadIssue> issues = new ArrayList<>();
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
token = reader.peekToken();
|
TweedDataToken token = reader.peekToken();
|
||||||
if (token.isListEnd()) {
|
if (token.isListEnd()) {
|
||||||
reader.readToken();
|
reader.readToken();
|
||||||
break;
|
break;
|
||||||
} else if (token.isListValue()) {
|
} else if (token.isListValue()) {
|
||||||
list.add(elementReader.read(reader, elementEntry, context));
|
TweedReadResult<T> elementResult = elementReader.read(reader, elementEntry, context);
|
||||||
|
issues.addAll(Arrays.asList(elementResult.issues()));
|
||||||
|
if (elementResult.isFailed() || elementResult.isError()) {
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
|
} else if (elementResult.hasValue()) {
|
||||||
|
list.add(elementResult.value());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException(
|
issues.add(TweedReadIssue.error(
|
||||||
"Unexpected token " + token + ": expected next list value or list end",
|
"Unexpected token " + token + ": expected next list value or list end",
|
||||||
context
|
context
|
||||||
);
|
));
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
}
|
}
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException(
|
issues.add(TweedReadIssue.error(e, context));
|
||||||
"Failed reading element " + list.size() + " of collection",
|
|
||||||
e,
|
|
||||||
context
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
C result = entry.instantiateCollection(list.size());
|
C result = entry.instantiateCollection(list.size());
|
||||||
result.addAll(list);
|
result.addAll(list);
|
||||||
return result;
|
return TweedReadResult.ok(result);
|
||||||
|
}, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -226,16 +219,11 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
public static class CompoundReaderWriter<T> implements TweedEntryReaderWriter<T, CompoundConfigEntry<T>> {
|
public static class CompoundReaderWriter<T> implements TweedEntryReaderWriter<T, CompoundConfigEntry<T>> {
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, CompoundConfigEntry<T> entry, TweedReadContext context) throws
|
public TweedReadResult<T> read(TweedDataReader reader, CompoundConfigEntry<T> entry, TweedReadContext context) {
|
||||||
TweedEntryReadException {
|
return requireToken(reader, TweedDataToken::isMapStart, "Expected map start", context).andThen(_token -> {
|
||||||
try {
|
|
||||||
assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start", context);
|
|
||||||
} catch (TweedDataReadException e) {
|
|
||||||
throw new TweedEntryReadException("Failed reading compound start", e, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
||||||
T compoundValue = entry.instantiateValue();
|
T compoundValue = entry.instantiateValue();
|
||||||
|
List<TweedReadIssue> issues = new ArrayList<>();
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
TweedDataToken token = reader.readToken();
|
TweedDataToken token = reader.readToken();
|
||||||
@@ -247,24 +235,38 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
ConfigEntry<Object> subEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
ConfigEntry<Object> subEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
||||||
if (subEntry == null) {
|
if (subEntry == null) {
|
||||||
//noinspection DataFlowIssue
|
TweedReadResult<Object> noopResult = NOOP_READER_WRITER.read(reader, null, context);
|
||||||
NOOP_READER_WRITER.read(reader, null, context);
|
issues.addAll(Arrays.asList(noopResult.issues()));
|
||||||
|
if (noopResult.isFailed()) {
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
val subEntryReaderChain = context.readWriteExtension().getReaderChain(subEntry);
|
val subEntryReaderChain = context.readWriteExtension().getReaderChain(subEntry);
|
||||||
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
TweedReadResult<Object> subEntryResult = subEntryReaderChain.read(reader, subEntry, context);
|
||||||
entry.set(compoundValue, key, subEntryValue);
|
issues.addAll(Arrays.asList(subEntryResult.issues()));
|
||||||
|
if (subEntryResult.isFailed() || subEntryResult.isError()) {
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
|
} else if (subEntryResult.hasValue()) {
|
||||||
|
entry.set(compoundValue, key, subEntryResult.value());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException(
|
issues.add(TweedReadIssue.error(
|
||||||
"Unexpected token " + token + ": Expected map key or map end",
|
"Unexpected token " + token + ": Expected map key or map end",
|
||||||
context
|
context
|
||||||
);
|
));
|
||||||
|
return TweedReadResult.failed(issues.toArray(new TweedReadIssue[0]));
|
||||||
}
|
}
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException("Failed reading compound entry", e, context);
|
throw new TweedEntryReadException("Failed reading compound entry", e, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compoundValue;
|
if (issues.isEmpty()) {
|
||||||
|
return TweedReadResult.ok(compoundValue);
|
||||||
|
} else {
|
||||||
|
return TweedReadResult.withIssues(compoundValue, issues.toArray(new TweedReadIssue[0]));
|
||||||
|
}
|
||||||
|
}, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -291,12 +293,11 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
public static class NoopReaderWriter implements TweedEntryReaderWriter<@Nullable Object, ConfigEntry<Object>> {
|
public static class NoopReaderWriter implements TweedEntryReaderWriter<@Nullable Object, ConfigEntry<Object>> {
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Object read(TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context)
|
public TweedReadResult<@Nullable Object> read(TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) {
|
||||||
throws TweedEntryReadException {
|
|
||||||
try {
|
try {
|
||||||
TweedDataToken token = reader.readToken();
|
TweedDataToken token = reader.readToken();
|
||||||
if (!token.isListStart() && !token.isMapStart()) {
|
if (!token.isListStart() && !token.isMapStart()) {
|
||||||
return null;
|
return TweedReadResult.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayDeque<Context> stack = new ArrayDeque<>(20);
|
ArrayDeque<Context> stack = new ArrayDeque<>(20);
|
||||||
@@ -316,11 +317,11 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
if (stack.isEmpty()) {
|
if (stack.isEmpty()) {
|
||||||
return null;
|
return TweedReadResult.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException("Failed skipping through value", e, context);
|
return TweedReadResult.failed(TweedReadIssue.error(e, context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,14 +353,23 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertIsToken(
|
private static TweedReadResult<TweedDataToken> requireToken(
|
||||||
TweedDataToken token,
|
TweedDataReader reader,
|
||||||
Predicate<TweedDataToken> isToken,
|
Predicate<TweedDataToken> isToken,
|
||||||
String description,
|
String description,
|
||||||
TweedReadContext context
|
TweedReadContext context
|
||||||
) throws TweedEntryReadException {
|
) {
|
||||||
if (!isToken.test(token)) {
|
try {
|
||||||
throw new TweedEntryReadException("Unexpected token " + token + ": " + description, context);
|
TweedDataToken token = reader.readToken();
|
||||||
|
if (isToken.test(token)) {
|
||||||
|
return TweedReadResult.ok(token);
|
||||||
|
}
|
||||||
|
return TweedReadResult.failed(TweedReadIssue.error(
|
||||||
|
"Unexpected token " + token + ": " + description,
|
||||||
|
context
|
||||||
|
));
|
||||||
|
} catch (TweedDataReadException e) {
|
||||||
|
return TweedReadResult.failed(TweedReadIssue.error(e, context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
|||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class ReadWriteExtensionImplTest {
|
class ReadWriteExtensionImplTest {
|
||||||
private final StringWriter stringWriter = new StringWriter();
|
private final StringWriter stringWriter = new StringWriter();
|
||||||
@@ -97,7 +96,7 @@ class ReadWriteExtensionImplTest {
|
|||||||
@Test
|
@Test
|
||||||
void read() {
|
void read() {
|
||||||
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class).orElseThrow();
|
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class).orElseThrow();
|
||||||
Map<String, Object> result = assertDoesNotThrow(() -> readWriteExtension.read(
|
var result = assertDoesNotThrow(() -> readWriteExtension.read(
|
||||||
new HjsonReader(new HjsonLexer(new StringReader("""
|
new HjsonReader(new HjsonLexer(new StringReader("""
|
||||||
{
|
{
|
||||||
\tint: 123
|
\tint: 123
|
||||||
@@ -112,9 +111,12 @@ class ReadWriteExtensionImplTest {
|
|||||||
readWriteExtension.createReadWriteContextExtensionsData()
|
readWriteExtension.createReadWriteContextExtensionsData()
|
||||||
));
|
));
|
||||||
|
|
||||||
assertEquals(2, result.size());
|
assertThat(result.isFailed()).isFalse();
|
||||||
assertEquals(123, result.get("int"));
|
assertThat(result.hasValue()).isTrue();
|
||||||
assertEquals(Arrays.asList(true, false, true), result.get("list"));
|
assertThat(result.value()).isEqualTo(Map.of(
|
||||||
|
"int", 123,
|
||||||
|
"list", Arrays.asList(true, false, true)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TweedDataVisitor setupWriter(Function<Writer, TweedDataVisitor> writerFactory) {
|
private TweedDataVisitor setupWriter(Function<Writer, TweedDataVisitor> writerFactory) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import de.siphalor.tweed5.serde.extension.api.ReadWriteExtension;
|
|||||||
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
import de.siphalor.tweed5.serde.extension.api.TweedEntryWriter;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedReaderWriterProvider;
|
import de.siphalor.tweed5.serde.extension.api.TweedReaderWriterProvider;
|
||||||
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
|
import de.siphalor.tweed5.serde.extension.api.TweedWriteContext;
|
||||||
|
import de.siphalor.tweed5.serde.extension.api.read.result.TweedReadResult;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
import de.siphalor.tweed5.serde.hjson.HjsonLexer;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
import de.siphalor.tweed5.serde.hjson.HjsonReader;
|
||||||
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
import de.siphalor.tweed5.serde.hjson.HjsonWriter;
|
||||||
@@ -55,7 +56,7 @@ class ReadWritePojoWeavingProcessorTest {
|
|||||||
}"""
|
}"""
|
||||||
)));
|
)));
|
||||||
assertThat(configContainer.rootEntry().call(read(reader)))
|
assertThat(configContainer.rootEntry().call(read(reader)))
|
||||||
.isEqualTo(new AnnotatedConfig(987, "abdef", new TestClass(29)));
|
.isEqualTo(TweedReadResult.ok(new AnnotatedConfig(987, "abdef", new TestClass(29))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoService(TweedReaderWriterProvider.class)
|
@AutoService(TweedReaderWriterProvider.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user