diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java index d0daf64b..2e85b043 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java +++ b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java @@ -43,7 +43,7 @@ public class MVWorldListener implements InjectableListener { return; } worldManager.unloadWorld(event.getWorld()).onFailure(failure -> { - if (failure.getFailureReason() != UnloadWorldResult.Failure.WORLD_ALREADY_UNLOADING) { + if (failure.getFailureReason() != UnloadWorldResult.WORLD_ALREADY_UNLOADING) { Logging.severe("Failed to unload world: " + failure); } }); @@ -60,7 +60,7 @@ public class MVWorldListener implements InjectableListener { .peek(world -> { Logging.fine("Loading world: " + world.getName()); worldManager.loadWorld(world).onFailure(failure -> { - if (failure.getFailureReason() != LoadWorldResult.Failure.WORLD_ALREADY_LOADING) { + if (failure.getFailureReason() != LoadWorldResult.WORLD_ALREADY_LOADING) { Logging.severe("Failed to load world: " + failure); } }); diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/result/Attempt.java b/src/main/java/com/onarandombox/MultiverseCore/utils/result/Attempt.java index d7c2ff98..177a57cc 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/result/Attempt.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/result/Attempt.java @@ -7,35 +7,98 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -public interface Attempt { +/** + * Represents an attempt to process a value that can fail with a reason that has a localized message. + * + * @param The type of the value. + * @param The type of failure reason. + */ +public sealed interface Attempt permits Attempt.Success, Attempt.Failure { + /** + * Creates a new success attempt. + * + * @param value The value. + * @param The type of the value. + * @param The type of failure reason. + * @return The new success attempt. + */ static Attempt success(T value) { return new Success<>(value); } + /** + * Creates a new failure attempt. + * + * @param failureReason The reason for failure. + * @param messageReplacements The replacements for the failure message. + * @param The type of the value. + * @param The type of failure reason. + * @return The new failure attempt. + */ static Attempt failure( F failureReason, MessageReplacement... messageReplacements) { return new Failure<>(failureReason, Message.of(failureReason, "Failed!", messageReplacements)); } + /** + * Creates a new failure attempt with a custom message. + * + * @param failureReason The reason for failure. + * @param message The custom message for failure. This will override the default message. + * @param The type of the value. + * @param The type of failure reason. + * @return The new failure attempt. + */ static Attempt failure(F failureReason, Message message) { return new Failure<>(failureReason, message); } + /** + * Gets the value of this attempt. Exceptions will be thrown if this is a failure attempt. + * + * @return The value. + */ T get(); + /** + * Gets the reason for failure. Exceptions will be thrown if this is a success attempt. + * + * @return The reason for failure. + */ F getFailureReason(); + /** + * Gets the message for failure. Exceptions will be thrown if this is a success attempt. + * + * @return The message for failure. + */ Message getFailureMessage(); + /** + * Returns whether this attempt is a success. + * + * @return Whether this attempt is a success. + */ default boolean isSuccess() { return this instanceof Success; } + /** + * Returns whether this attempt is a failure. + * + * @return Whether this attempt is a failure. + */ default boolean isFailure() { return this instanceof Failure; } + /** + * Peeks at the value if this is a success attempt. + * + * @param consumer The consumer with the value. + * @return This attempt. + */ default Attempt peek(Consumer consumer) { if (this instanceof Success) { consumer.accept(get()); @@ -43,6 +106,13 @@ public interface Attempt { return this; } + /** + * Maps the value to another value if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ default Attempt map(Function mapper) { if (this instanceof Success) { return new Success<>(mapper.apply(get())); @@ -51,6 +121,13 @@ public interface Attempt { } } + /** + * Maps the value to another attempt if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ default Attempt map(Supplier mapper) { if (this instanceof Success) { return new Success<>(mapper.get()); @@ -59,6 +136,13 @@ public interface Attempt { } } + /** + * Maps the value to another attempt with the same fail reason if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ default Attempt mapAttempt(Function> mapper) { if (this instanceof Success) { return mapper.apply(get()); @@ -67,6 +151,13 @@ public interface Attempt { } } + /** + * Maps the value to another attempt with the same fail reason if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ default Attempt mapAttempt(Supplier> mapper) { if (this instanceof Success) { return mapper.get(); @@ -75,6 +166,13 @@ public interface Attempt { } } + /** + * Maps to another attempt with a different fail reason. + * + * @param failureReason The new fail reason. + * @param The type of the new fail reason. + * @return The new attempt. + */ default Attempt transform(UF failureReason) { if (this instanceof Success) { return new Success<>(get()); @@ -83,14 +181,14 @@ public interface Attempt { } } - default Attempt convertReason(NF failure) { - if (this instanceof Success) { - return new Success<>(get()); - } else { - return new Failure<>(failure, getFailureMessage()); - } - } - + /** + * Calls either the failure or success function depending on the result type. + * + * @param failureMapper The failure function. + * @param successMapper The success function. + * @param The type of the new value. + * @return The result of the function. + */ default N fold(Function, N> failureMapper, Function successMapper) { if (this instanceof Success) { return successMapper.apply(get()); @@ -99,6 +197,12 @@ public interface Attempt { } } + /** + * Calls the given runnable if this is a success attempt. + * + * @param runnable The runnable. + * @return This attempt. + */ default Attempt onSuccess(Runnable runnable) { if (this instanceof Success) { runnable.run(); @@ -106,6 +210,12 @@ public interface Attempt { return this; } + /** + * Calls the given consumer if this is a success attempt. + * + * @param consumer The consumer with the value. + * @return This attempt. + */ default Attempt onSuccess(Consumer consumer) { if (this instanceof Success) { consumer.accept(get()); @@ -113,6 +223,12 @@ public interface Attempt { return this; } + /** + * Calls the given consumer if this is a failure attempt. + * + * @param runnable The runnable. + * @return This attempt. + */ default Attempt onFailure(Runnable runnable) { if (this instanceof Failure) { runnable.run(); @@ -120,6 +236,12 @@ public interface Attempt { return this; } + /** + * Calls the given consumer if this is a failure attempt. + * + * @param consumer The consumer with the failure instance. + * @return This attempt. + */ default Attempt onFailure(Consumer> consumer) { if (this instanceof Failure) { consumer.accept((Failure) this); @@ -127,6 +249,12 @@ public interface Attempt { return this; } + /** + * Calls the given runnable if this is a failure attempt. + * + * @param consumer The consumer with the failure reason. + * @return This attempt. + */ default Attempt onFailureReason(Consumer consumer) { if (this instanceof Failure) { consumer.accept(getFailureReason()); @@ -134,7 +262,13 @@ public interface Attempt { return this; } - class Success implements Attempt { + /** + * Represents a successful attempt with a value. + * + * @param The type of the value. + * @param The type of failure reason. + */ + final class Success implements Attempt { private final T value; Success(T value) { @@ -148,12 +282,12 @@ public interface Attempt { @Override public F getFailureReason() { - throw new IllegalStateException("Attempt is a success!"); + throw new UnsupportedOperationException("No failure reason as attempt is a success"); } @Override public Message getFailureMessage() { - throw new IllegalStateException("Attempt is a success!"); + throw new UnsupportedOperationException("No failure message as attempt is a success"); } @Override @@ -164,7 +298,13 @@ public interface Attempt { } } - class Failure implements Attempt { + /** + * Represents a failed attempt with a reason. + * + * @param The type of the value. + * @param The type of failure reason. + */ + final class Failure implements Attempt { private final F failureReason; private final Message message; @@ -175,7 +315,7 @@ public interface Attempt { @Override public T get() { - throw new IllegalStateException("Attempt is a failure!"); + throw new UnsupportedOperationException("No value as attempt is a failure"); } @Override diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/result/Result.java b/src/main/java/com/onarandombox/MultiverseCore/utils/result/Result.java index 6e029943..479f0854 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/result/Result.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/result/Result.java @@ -8,42 +8,123 @@ import java.util.NoSuchElementException; import java.util.function.Consumer; import java.util.function.Function; -public sealed interface Result permits Result.Success, Result.Failure { +/** + * Represents result of an operation with a reason for success or failure that has localized messages. + * + * @param The type of success reason. + * @param The type of failure reason. + */ +public sealed interface Result + permits Result.Success, Result.Failure { + /** + * Creates a new success result. + * + * @param successReason The reason for success. + * @param replacements The replacements for the success message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new success result. + */ static Result success( - S successReason, MessageReplacement...replacements) { + S successReason, MessageReplacement... replacements) { return new Success<>(successReason, replacements); } + /** + * Creates a new success result. + * + * @param successReason The reason for success. + * @param message The custom message for success. This will override the default message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new success result. + */ static Result success(S successReason, Message message) { return new Success<>(successReason, message); } + /** + * Creates a new failure result. + * + * @param failureReason The reason for failure. + * @param replacements The replacements for the failure message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new failure result. + */ static Result failure( - F failureReason, MessageReplacement...replacements) { + F failureReason, MessageReplacement... replacements) { return new Failure<>(failureReason, replacements); } + /** + * Creates a new failure result. + * + * @param failureReason The reason for failure. + * @param message The custom message for failure. This will override the default message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new failure result. + */ static Result failure(F failureReason, Message message) { return new Failure<>(failureReason, message); } + + /** + * Returns whether this result is a success. + * + * @return Whether this result is a success. + */ boolean isSuccess(); + /** + * Returns whether this result is a failure. + * + * @return Whether this result is a failure. + */ boolean isFailure(); + /** + * Returns the reason for success. + * + * @return The reason for success. + */ S getSuccessReason(); + /** + * Returns the reason for failure. + * + * @return The reason for failure. + */ F getFailureReason(); + /** + * Returns the message for the reason of this result. + * + * @return The message for the reason. + */ @NotNull Message getReasonMessage(); - default Result onSuccess(Consumer> consumer) { + /** + * Executes the given consumer if this result is a success. + * + * @param consumer The consumer with success object. + * @return This result. + */ + default Result onSuccess(Consumer> consumer) { if (this instanceof Success) { - consumer.accept((Success) this); + consumer.accept((Success) this); } return this; } + /** + * Executes the given consumer if this result is a failure. + * + * @param consumer The consumer with failure object. + * @return This result. + */ default Result onFailure(Consumer> consumer) { if (this instanceof Failure) { consumer.accept((Failure) this); @@ -51,6 +132,13 @@ public sealed interface Result return this; } + /** + * Executes the given consumer if this result is a success and the success reason matches the given reason. + * + * @param successReason The success reason to match. + * @param consumer The consumer with success reason. + * @return This result. + */ default Result onSuccessReason(S successReason, Consumer consumer) { if (this.isSuccess() && this.getSuccessReason() == successReason) { consumer.accept(this.getSuccessReason()); @@ -58,6 +146,13 @@ public sealed interface Result return this; } + /** + * Executes the given consumer if this result is a failure and the failure reason matches the given reason. + * + * @param failureReason The failure reason to match. + * @param consumer The consumer with failure reason. + * @return This result. + */ default Result onFailureReason(F failureReason, Consumer consumer) { if (this.isFailure() && this.getFailureReason() == failureReason) { consumer.accept(this.getFailureReason()); @@ -65,13 +160,25 @@ public sealed interface Result return this; } - default Result onSuccessThen(Function, Result> function) { + /** + * Executes the given function if this result is a success and returns the result of the function. + * + * @param function The function with success object. + * @return The result of the function. + */ + default Result onSuccessThen(Function, Result> function) { if (this instanceof Success) { - return function.apply((Success) this); + return function.apply((Success) this); } return this; } + /** + * Executes the given function if this result is a failure and returns the result of the function. + * + * @param function The function with failure object. + * @return The result of the function. + */ default Result onFailureThen(Function, Result> function) { if (this instanceof Failure) { return function.apply((Failure) this); @@ -79,25 +186,39 @@ public sealed interface Result return this; } - default R fold(Function, R> failureFunc, Function, R> successFunc) { + /** + * Executes either the failure or success function depending on the result type. + * + * @param failureFunc The function with success reason. + * @param successFunc The function with success reason. + * @param The type of the result. + * @return The result of the function. + */ + default R fold(Function, R> failureFunc, Function, R> successFunc) { if (this instanceof Failure) { return failureFunc.apply((Failure) this); } else if (this instanceof Success) { - return successFunc.apply((Success) this); + return successFunc.apply((Success) this); } throw new IllegalStateException("Unknown result type: " + this.getClass().getName()); } - final class Success implements Result { + /** + * The class for a successful result. + * + * @param The type of failure reason. + * @param The type of success reason. + */ + final class Success implements Result { private final S successReason; private final Message message; - public Success(S successReason, Message message) { + Success(S successReason, Message message) { this.successReason = successReason; this.message = message; } - public Success(S successReason, MessageReplacement[] replacements) { + Success(S successReason, MessageReplacement[] replacements) { this.successReason = successReason; this.message = Message.of(successReason, "Success!", replacements); } @@ -119,7 +240,7 @@ public sealed interface Result @Override public F getFailureReason() { - throw new NoSuchElementException("No reason for failure"); + throw new UnsupportedOperationException("No reason for success"); } @Override @@ -135,18 +256,24 @@ public sealed interface Result } } + /** + * The class for a failed result. + * + * @param The type of success reason. + * @param The type of failure reason. + */ final class Failure implements Result { private final F failureReason; private final Message message; - public Failure(F failureReason, Message message) { + Failure(F failureReason, Message message) { this.failureReason = failureReason; this.message = message; } - public Failure(F failureReason, MessageReplacement[] replacements) { + Failure(F failureReason, MessageReplacement[] replacements) { this.failureReason = failureReason; - this.message = Message.of(failureReason, "Success!", replacements); + this.message = Message.of(failureReason, "Failed!", replacements); } @Override @@ -161,7 +288,7 @@ public sealed interface Result @Override public S getSuccessReason() { - throw new NoSuchElementException("No reason for failure"); + throw new UnsupportedOperationException("No reason for failure"); } @Override diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java index 64001b9d..8a16d24e 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java @@ -488,7 +488,7 @@ public class WorldManager { public Attempt deleteWorld(@NotNull MultiverseWorld world) { return getLoadedWorld(world).fold( () -> loadWorld(world) - .convertReason(DeleteWorldResult.LOAD_FAILED) + .transform(DeleteWorldResult.LOAD_FAILED) .mapAttempt(this::deleteWorld), this::deleteWorld); } @@ -535,7 +535,7 @@ public class WorldManager { .worldName(validatedOptions.newWorldName()) .environment(validatedOptions.world().getEnvironment()) .generator(validatedOptions.world().getGenerator()); - return importWorld(importWorldOptions).convertReason(CloneWorldResult.IMPORT_FAILED); + return importWorld(importWorldOptions).transform(CloneWorldResult.IMPORT_FAILED); }) .onSuccess(newWorld -> { cloneWorldTransferData(options, newWorld); @@ -616,8 +616,8 @@ public class WorldManager { .worldType(world.getWorldType().getOrElse(WorldType.NORMAL)); return deleteWorld(world) - .convertReason(RegenWorldResult.DELETE_FAILED) - .mapAttempt(() -> createWorld(createWorldOptions).convertReason(RegenWorldResult.CREATE_FAILED)) + .transform(RegenWorldResult.DELETE_FAILED) + .mapAttempt(() -> createWorld(createWorldOptions).transform(RegenWorldResult.CREATE_FAILED)) .onSuccess(newWorld -> { dataTransfer.pasteAllTo(newWorld); saveWorldsConfig();