[serde-extension] Improve exception handling
This commit is contained in:
@@ -4,10 +4,7 @@ import com.google.auto.service.AutoService;
|
|||||||
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.core.api.middleware.Middleware;
|
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
import de.siphalor.tweed5.data.extension.api.*;
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
|
||||||
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
|
|
||||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||||
@@ -18,6 +15,7 @@ import de.siphalor.tweed5.defaultextensions.pather.api.PathTrackingDataVisitor;
|
|||||||
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension;
|
||||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
import lombok.var;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
@@ -84,7 +82,21 @@ public class PatherExtensionImpl implements PatherExtension, TweedExtension, Rea
|
|||||||
|
|
||||||
pathTracking = new PathTracking();
|
pathTracking = new PathTracking();
|
||||||
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) {
|
||||||
|
var exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||||
|
if (exceptionPathTracking != null) {
|
||||||
|
throw new TweedEntryReadException(
|
||||||
|
"Exception while reading entry at "
|
||||||
|
+ String.join("/", exceptionPathTracking.currentPath())
|
||||||
|
+ ": " + e.getMessage(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -113,7 +125,21 @@ public class PatherExtensionImpl implements PatherExtension, TweedExtension, Rea
|
|||||||
|
|
||||||
pathTracking = new PathTracking();
|
pathTracking = new PathTracking();
|
||||||
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
context.extensionsData().set(rwContextPathTrackingAccess, pathTracking);
|
||||||
|
try {
|
||||||
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
castedInner.write(new PathTrackingDataVisitor(writer, pathTracking), value, entry, context);
|
||||||
|
} catch (TweedEntryWriteException e) {
|
||||||
|
var exceptionPathTracking = e.context().extensionsData().get(rwContextPathTrackingAccess);
|
||||||
|
if (exceptionPathTracking != null) {
|
||||||
|
throw new TweedEntryWriteException(
|
||||||
|
"Exception while writing entry at "
|
||||||
|
+ String.join("/", exceptionPathTracking.currentPath())
|
||||||
|
+ ": " + e.getMessage(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -249,7 +249,10 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (validationResult.hasError()) {
|
if (validationResult.hasError()) {
|
||||||
throw new TweedEntryReadException("Failed to validate entry: " + validationResult.issues());
|
throw new TweedEntryReadException(
|
||||||
|
"Failed to validate entry: " + validationResult.issues(),
|
||||||
|
context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationResult.value();
|
return validationResult.value();
|
||||||
|
|||||||
@@ -1,18 +1,28 @@
|
|||||||
package de.siphalor.tweed5.data.extension.api;
|
package de.siphalor.tweed5.data.extension.api;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public class TweedEntryReadException extends Exception {
|
public class TweedEntryReadException extends Exception {
|
||||||
public TweedEntryReadException() {
|
private final TweedReadContext context;
|
||||||
}
|
|
||||||
|
|
||||||
public TweedEntryReadException(String message) {
|
public TweedEntryReadException(String message, TweedReadContext context) {
|
||||||
super(message);
|
super(message);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TweedEntryReadException(String message, Throwable cause) {
|
public TweedEntryReadException(String message, Throwable cause, TweedReadContext context) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TweedEntryReadException(Throwable cause) {
|
public TweedEntryReadException(String message, TweedEntryReadException cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.context = cause.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweedEntryReadException(Throwable cause, TweedReadContext context) {
|
||||||
super(cause);
|
super(cause);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package de.siphalor.tweed5.data.extension.api;
|
package de.siphalor.tweed5.data.extension.api;
|
||||||
|
|
||||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
|
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
import de.siphalor.tweed5.dataapi.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
|
T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException;
|
||||||
TweedEntryReadException,
|
|
||||||
TweedDataReadException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,28 @@
|
|||||||
package de.siphalor.tweed5.data.extension.api;
|
package de.siphalor.tweed5.data.extension.api;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public class TweedEntryWriteException extends Exception {
|
public class TweedEntryWriteException extends Exception {
|
||||||
public TweedEntryWriteException() {
|
private final TweedWriteContext context;
|
||||||
}
|
|
||||||
|
|
||||||
public TweedEntryWriteException(String message) {
|
public TweedEntryWriteException(String message, TweedWriteContext context) {
|
||||||
super(message);
|
super(message);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TweedEntryWriteException(String message, Throwable cause) {
|
public TweedEntryWriteException(String message, Throwable cause, TweedWriteContext context) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TweedEntryWriteException(Throwable cause) {
|
public TweedEntryWriteException(String message, TweedEntryWriteException cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.context = cause.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweedEntryWriteException(Throwable cause, TweedWriteContext context) {
|
||||||
super(cause);
|
super(cause);
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,10 +126,11 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
|||||||
ConfigEntry<T> entry,
|
ConfigEntry<T> entry,
|
||||||
Patchwork contextExtensionsData
|
Patchwork contextExtensionsData
|
||||||
) throws TweedEntryReadException {
|
) throws TweedEntryReadException {
|
||||||
|
TweedReadContext context = new TweedReadWriteContextImpl(this, contextExtensionsData);
|
||||||
try {
|
try {
|
||||||
return getReaderChain(entry).read(reader, entry, new TweedReadWriteContextImpl(this, contextExtensionsData));
|
return getReaderChain(entry).read(reader, entry, context);
|
||||||
} catch (TweedDataReadException e) {
|
} catch (TweedDataReadException e) {
|
||||||
throw new TweedEntryReadException("Failed to read entry", e);
|
throw new TweedEntryReadException("Failed to read entry", e, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,10 +141,11 @@ public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
|||||||
ConfigEntry<T> entry,
|
ConfigEntry<T> entry,
|
||||||
Patchwork contextExtensionsData
|
Patchwork contextExtensionsData
|
||||||
) throws TweedEntryWriteException {
|
) throws TweedEntryWriteException {
|
||||||
|
TweedWriteContext context = new TweedReadWriteContextImpl(this, contextExtensionsData);
|
||||||
try {
|
try {
|
||||||
getWriterChain(entry).write(writer, value, entry, new TweedReadWriteContextImpl(this, contextExtensionsData));
|
getWriterChain(entry).write(writer, value, entry, context);
|
||||||
} catch (TweedDataWriteException e) {
|
} catch (TweedDataWriteException e) {
|
||||||
throw new TweedEntryWriteException("Failed to write entry", e);
|
throw new TweedEntryWriteException("Failed to write entry", e, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ 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, TweedDataReadException {
|
public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException {
|
||||||
if (reader.peekToken().isNull()) {
|
if (reader.peekToken().isNull()) {
|
||||||
reader.readToken();
|
reader.readToken();
|
||||||
return null;
|
return null;
|
||||||
@@ -67,21 +67,22 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
private final BiConsumer<TweedDataVisitor, T> writerCall;
|
private final BiConsumer<TweedDataVisitor, T> writerCall;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) throws TweedDataReadException {
|
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) {
|
||||||
return readerCall.apply(reader.readToken());
|
return readerCall.apply(reader.readToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(TweedDataVisitor writer, @Nullable T value, ConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
public void write(TweedDataVisitor writer, @Nullable T value, ConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||||
requireNonNullWriteValue(value);
|
requireNonNullWriteValue(value, context);
|
||||||
writerCall.accept(writer, value);
|
writerCall.accept(writer, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 TweedEntryReadException, TweedDataReadException {
|
public C read(TweedDataReader reader, CollectionConfigEntry<T, C> entry, TweedReadContext context) throws
|
||||||
assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start");
|
TweedEntryReadException {
|
||||||
|
assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start", context);
|
||||||
TweedDataToken token = reader.peekToken();
|
TweedDataToken token = reader.peekToken();
|
||||||
if (token.isListEnd()) {
|
if (token.isListEnd()) {
|
||||||
return entry.instantiateCollection(0);
|
return entry.instantiateCollection(0);
|
||||||
@@ -99,7 +100,10 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
} else if (token.isListValue()) {
|
} else if (token.isListValue()) {
|
||||||
list.add(elementReader.read(reader, elementEntry, context));
|
list.add(elementReader.read(reader, elementEntry, context));
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException("Unexpected token " + token + ": expected next list value or list end");
|
throw new TweedEntryReadException(
|
||||||
|
"Unexpected token " + token + ": expected next list value or list end",
|
||||||
|
context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +114,7 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(TweedDataVisitor writer, C value, CollectionConfigEntry<T, C> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
public void write(TweedDataVisitor writer, C value, CollectionConfigEntry<T, C> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||||
requireNonNullWriteValue(value);
|
requireNonNullWriteValue(value, context);
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
writer.visitEmptyList();
|
writer.visitEmptyList();
|
||||||
@@ -130,8 +134,9 @@ 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 TweedEntryReadException, TweedDataReadException {
|
public T read(TweedDataReader reader, CompoundConfigEntry<T> entry, TweedReadContext context) throws
|
||||||
assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start");
|
TweedEntryReadException {
|
||||||
|
assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start", context);
|
||||||
|
|
||||||
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
||||||
T compoundValue = entry.instantiateCompoundValue();
|
T compoundValue = entry.instantiateCompoundValue();
|
||||||
@@ -148,7 +153,10 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
||||||
entry.set(compoundValue, key, subEntryValue);
|
entry.set(compoundValue, key, subEntryValue);
|
||||||
} else {
|
} else {
|
||||||
throw new TweedEntryReadException("Unexpected token " + token + ": Expected map key or map end");
|
throw new TweedEntryReadException(
|
||||||
|
"Unexpected token " + token + ": Expected map key or map end",
|
||||||
|
context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compoundValue;
|
return compoundValue;
|
||||||
@@ -156,7 +164,7 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(TweedDataVisitor writer, @Nullable T value, CompoundConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
public void write(TweedDataVisitor writer, @Nullable T value, CompoundConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||||
requireNonNullWriteValue(value);
|
requireNonNullWriteValue(value, context);
|
||||||
|
|
||||||
writer.visitMapStart();
|
writer.visitMapStart();
|
||||||
|
|
||||||
@@ -178,7 +186,7 @@ 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) throws TweedDataReadException {
|
public @Nullable Object read(TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) {
|
||||||
TweedDataToken token = reader.readToken();
|
TweedDataToken token = reader.readToken();
|
||||||
if (!token.isListStart() && !token.isMapStart()) {
|
if (!token.isListStart() && !token.isMapStart()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -216,16 +224,24 @@ public class TweedEntryReaderWriterImpls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> fail")
|
@Contract("null, _ -> fail")
|
||||||
private static <T> void requireNonNullWriteValue(@Nullable T value) throws TweedEntryWriteException {
|
private static <T> void requireNonNullWriteValue(
|
||||||
|
@Nullable T value,
|
||||||
|
TweedWriteContext context
|
||||||
|
) throws TweedEntryWriteException {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new TweedEntryWriteException("Unable to write null value");
|
throw new TweedEntryWriteException("Unable to write null value", context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertIsToken(TweedDataToken token, Predicate<TweedDataToken> isToken, String description) throws TweedEntryReadException {
|
private static void assertIsToken(
|
||||||
|
TweedDataToken token,
|
||||||
|
Predicate<TweedDataToken> isToken,
|
||||||
|
String description,
|
||||||
|
TweedReadContext context
|
||||||
|
) throws TweedEntryReadException {
|
||||||
if (!isToken.test(token)) {
|
if (!isToken.test(token)) {
|
||||||
throw new TweedEntryReadException("Unexpected token " + token + ": " + description);
|
throw new TweedEntryReadException("Unexpected token " + token + ": " + description, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user