From efc9d8647b05ef26ab293981c37b2eac67936cdb Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sun, 9 Nov 2025 21:47:45 +0100 Subject: [PATCH] feat(default-exts): Implement read fallback extension --- .../api/ReadFallbackExtension.java | 14 ++++ .../impl/ReadFallbackExtensionImpl.java | 71 +++++++++++++++++ .../impl/ReadFallbackExtensionImplTest.java | 76 +++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/api/ReadFallbackExtension.java create mode 100644 tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImpl.java create mode 100644 tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImplTest.java diff --git a/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/api/ReadFallbackExtension.java b/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/api/ReadFallbackExtension.java new file mode 100644 index 0000000..af9e7b3 --- /dev/null +++ b/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/api/ReadFallbackExtension.java @@ -0,0 +1,14 @@ +package de.siphalor.tweed5.defaultextensions.readfallback.api; + +import de.siphalor.tweed5.core.api.extension.TweedExtension; +import de.siphalor.tweed5.defaultextensions.readfallback.impl.ReadFallbackExtensionImpl; + +public interface ReadFallbackExtension extends TweedExtension { + Class DEFAULT = ReadFallbackExtensionImpl.class; + String EXTENSION_ID = "read-fallback"; + + @Override + default String getId() { + return EXTENSION_ID; + } +} diff --git a/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImpl.java b/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImpl.java new file mode 100644 index 0000000..bcd74b8 --- /dev/null +++ b/tweed5/default-extensions/src/main/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImpl.java @@ -0,0 +1,71 @@ +package de.siphalor.tweed5.defaultextensions.readfallback.impl; + +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.middleware.Middleware; +import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension; +import de.siphalor.tweed5.data.extension.api.TweedEntryReadException; +import de.siphalor.tweed5.data.extension.api.TweedEntryReader; +import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext; +import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension; +import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension; +import lombok.extern.apachecommons.CommonsLog; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.Collections; +import java.util.Set; + +@CommonsLog +public class ReadFallbackExtensionImpl implements ReadFallbackExtension, ReadWriteRelatedExtension { + private final ConfigContainer configContainer; + private @Nullable PresetsExtension presetsExtension; + + public ReadFallbackExtensionImpl(ConfigContainer configContainer) { + this.configContainer = configContainer; + } + + @Override + public void extensionsFinalized() { + presetsExtension = configContainer.extension(PresetsExtension.class) + .orElseThrow(() -> new IllegalStateException(getClass().getSimpleName() + + " requires " + ReadFallbackExtension.class.getSimpleName())); + } + + @Override + public void setupReadWriteExtension(ReadWriteExtensionSetupContext context) { + assert presetsExtension != null; + + context.registerReaderMiddleware(new Middleware>() { + @Override + public String id() { + return EXTENSION_ID; + } + + @Override + public Set mustComeBefore() { + return Collections.singleton(DEFAULT_START); + } + + @Override + public Set mustComeAfter() { + return Collections.emptySet(); + } + + @Override + public TweedEntryReader process(TweedEntryReader inner) { + //noinspection unchecked + TweedEntryReader> castedInner = + (TweedEntryReader>) inner; + return (TweedEntryReader>) (reader, entry, context1) -> { + try { + return castedInner.read(reader, entry, context1); + } catch (TweedEntryReadException e) { + log.error("Failed to read entry: " + e.getMessage(), e); + return presetsExtension.presetValue(entry, PresetsExtension.DEFAULT_PRESET_NAME); + } + }; + } + }); + } +} diff --git a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImplTest.java b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImplTest.java new file mode 100644 index 0000000..637f6f9 --- /dev/null +++ b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/readfallback/impl/ReadFallbackExtensionImplTest.java @@ -0,0 +1,76 @@ +package de.siphalor.tweed5.defaultextensions.readfallback.impl; + +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.impl.DefaultConfigContainer; +import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl; +import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; +import de.siphalor.tweed5.data.extension.api.TweedEntryReadException; +import de.siphalor.tweed5.data.extension.api.TweedReadContext; +import de.siphalor.tweed5.data.extension.api.TweedWriteContext; +import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter; +import de.siphalor.tweed5.data.hjson.HjsonLexer; +import de.siphalor.tweed5.data.hjson.HjsonReader; +import de.siphalor.tweed5.dataapi.api.*; +import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension; +import de.siphalor.tweed5.defaultextensions.readfallback.api.ReadFallbackExtension; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; + +import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.entryReaderWriter; +import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.read; +import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.presetValue; +import static org.assertj.core.api.Assertions.assertThat; + +@NullMarked +class ReadFallbackExtensionImplTest { + @Test + void test() { + DefaultConfigContainer configContainer = new DefaultConfigContainer<>(); + configContainer.registerExtension(ReadWriteExtension.class); + configContainer.registerExtension(PresetsExtension.class); + configContainer.registerExtension(ReadFallbackExtension.DEFAULT); + configContainer.finishExtensionSetup(); + + ConfigEntry entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) + .apply(presetValue(PresetsExtension.DEFAULT_PRESET_NAME, -1)) + .apply(entryReaderWriter(new TweedEntryReaderWriter<>() { + @Override + public Integer read( + TweedDataReader reader, + ConfigEntry entry, + TweedReadContext context + ) throws TweedEntryReadException { + int value; + try { + value = reader.readToken().readAsInt(); + } catch (TweedDataReadException e) { + throw new IllegalStateException("Should not be called", e); + } + if (value % 2 == 0) { + return value; + } else { + throw new TweedEntryReadException("Value is not even", context); + } + } + + @Override + public void write( + TweedDataVisitor writer, + @Nullable Integer value, + ConfigEntry entry, + TweedWriteContext context + ) throws TweedDataWriteException { + throw new IllegalStateException("Should not be called"); + } + })); + + configContainer.attachTree(entry); + configContainer.initialize(); + + assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("12")))))).isEqualTo(12); + assertThat(entry.call(read(new HjsonReader(new HjsonLexer(new StringReader("13")))))).isEqualTo(-1); + } +}