All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.ydb.core.Result Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
package tech.ydb.core;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import javax.annotation.Nonnull;


/**
 * @author Sergey Polovko
 * @author Alexandr Gorshenin
 * @param  type of result value
 */
public interface Result {
    @Nonnull
    Status getStatus();

    @Nonnull
    T getValue() throws UnexpectedResultException;

    @Nonnull
     Result map(Function mapper);

    @Nonnull
     CompletableFuture> mapResultFuture(Function>> mapper);

    @Nonnull
    CompletableFuture mapStatusFuture(Function> mapper);

    default boolean isSuccess() {
        return getStatus().getCode() == StatusCode.SUCCESS;
    }

    static  Result success(@Nonnull V value) {
        return new Success<>(value, Status.SUCCESS);
    }

    static  Result success(@Nonnull V value, Status status) {
        return new Success<>(value, Objects.requireNonNull(status));
    }

    static  Result fail(@Nonnull Status status) {
        return new Fail<>(Objects.requireNonNull(status));
    }

    static  Result fail(UnexpectedResultException unexpected) {
        return new Unexpected<>(Objects.requireNonNull(unexpected));
    }

    static  Result error(String message, Throwable throwable) {
        if (throwable != null && throwable instanceof UnexpectedResultException) {
            return new Unexpected<>(message, (UnexpectedResultException) throwable);
        }
        return new Error<>(message, throwable);
    }

    /**
     * Create functor to compose the successful result to next completable future with another result. Failed results
     * will be passed as is.
     * 

* This helper is designed to be used as {@link CompletableFuture#thenCompose(java.util.function.Function) } * argument *

* Example of usage: *

 {@code
     * // Execute one query, with opening new transaction
     * session.executeDataQuery(...)
     *    // Execute second query if first was successful
     *    .thenCompose(Result.compose(fisrt -> session.executeDataQuery(...)))
     *    // Commit transaction after two successful query executions
     *    .thenCompose(Result.composeStatus(second -> session.commitTransaction(...)));
     * }
* @param type of value in Result * @param type of resulting value in returning future * @param mapper mapper from successful value to completable future with another result * @return functor which composes successful results to completable future with another result */ static Function, CompletableFuture>> compose( Function>> mapper) { return result -> result.mapResultFuture(mapper); } /** * Create functor to compose the successful result to next completable future with status. Failed results * will be composed to its statuses. *

* This helper is designed to be used as {@link CompletableFuture#thenCompose(java.util.function.Function) } * argument *

* Example of usage: *

 {@code
     * // Execute one query, with opening new transaction
     * session.executeDataQuery(...)
     *    // Execute second query if first was successful
     *    .thenCompose(Result.compose(fisrt -> session.executeDataQuery(...)))
     *    // Commit transaction after two successful query executions
     *    .thenCompose(Result.composeStatus(second -> session.commitTransaction(...)));
     * }
* @param type of value in Result * @param mapper mapper from successful value to completable future with status * @return functor which composes successful results to completable future with status */ static Function, CompletableFuture> composeStatus( Function> mapper) { return result -> result.mapStatusFuture(mapper); } /** * Create functor to compose the successful result to completed future with specified value. Failed results * will be passed as is. *

* This helper is designed to be used as {@link CompletableFuture#thenCompose(java.util.function.Function) } * argument *

* Example of usage: *

 {@code
     * // Execute one query
     * session.executeDataQuery(...)
     *    // Execute second query if first was successful
     *    .thenCompose(Result.compose(fisrt -> session
     *        .executeDataQuery(...)
     *        // But use first request result as the result of
     *        .thenCompose(Result.composeValue(first))
     *    )
     * )
     * }
* @param type of value in Result * @param type of composed value * @param value value to create completed future * @return functor which composes successful results to completed future with specified value */ static Function, CompletableFuture>> composeValue(U value) { return result -> result.mapResultFuture(v -> CompletableFuture.completedFuture(Result.success(value))); } /* * SUCCESS */ final class Success implements Result { private final V value; private final Status status; private Success(V value, Status status) { assert status.getCode() == StatusCode.SUCCESS; this.value = value; this.status = status; } @Override public Status getStatus() { return status; } @Override public V getValue() { return value; } @Override public Success map(Function mapper) { return new Success<>(mapper.apply(value), status); } @Override public CompletableFuture> mapResultFuture(Function>> mapper) { return mapper.apply(value); } @Override public CompletableFuture mapStatusFuture(Function> mapper) { return mapper.apply(value); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Success success = (Success) o; return Objects.equals(status, success.status) && Objects.equals(value, success.value); } @Override public int hashCode() { return Objects.hash(status, value); } @Override public String toString() { return "Success{" + value + ", status=" + status + "}"; } } /* * FAIL */ final class Fail implements Result { private final Status status; private Fail(Status status) { assert status.getCode() != StatusCode.SUCCESS; this.status = status; } @Override @SuppressWarnings("unchecked") public Fail map(Function mapper) { return (Fail) this; } @Override @SuppressWarnings("unchecked") public CompletableFuture> mapResultFuture(Function>> mapper) { return CompletableFuture.completedFuture((Fail) this); } @Override public CompletableFuture mapStatusFuture(Function> mapper) { return CompletableFuture.completedFuture(status); } @Override public Status getStatus() { return status; } @Override public V getValue() { throw new UnexpectedResultException("Cannot get value", status); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Fail fail = (Fail) o; return Objects.equals(status, fail.status); } @Override public int hashCode() { return status.hashCode(); } @Override public String toString() { return "Fail{" + status + "}"; } } final class Unexpected implements Result { private final UnexpectedResultException cause; private Unexpected(UnexpectedResultException cause) { this.cause = cause; } private Unexpected(String message, UnexpectedResultException cause) { this.cause = (message == null || message.isEmpty()) ? cause : new UnexpectedResultException(message, cause); } @Override public Status getStatus() { return cause.getStatus(); } @Override public V getValue() { throw cause; } @Override @SuppressWarnings("unchecked") public Unexpected map(Function mapper) { return (Unexpected) this; } @Override @SuppressWarnings("unchecked") public CompletableFuture> mapResultFuture(Function>> mapper) { return CompletableFuture.completedFuture((Unexpected) this); } @Override public CompletableFuture mapStatusFuture(Function> mapper) { return CompletableFuture.completedFuture(cause.getStatus()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Unexpected error = (Unexpected) o; return Objects.equals(cause, error.cause); } @Override public int hashCode() { return Objects.hash(cause); } @Override public String toString() { StringBuilder sb = new StringBuilder("Unexpected{message='").append(cause.getMessage()).append("'"); if (cause.getCause() != null) { sb.append(", cause=").append(cause.getCause()); } return sb.append("}").toString(); } } /* * ERROR */ final class Error implements Result { private static final Status ERROR = Status.of(StatusCode.CLIENT_INTERNAL_ERROR); private final String message; private final Status status; private Error(String message, Throwable cause) { this.message = message; this.status = ERROR.withCause(cause); } @Override public Status getStatus() { return status; } @Override public V getValue() { throw new UnexpectedResultException(message, status); } @Override @SuppressWarnings("unchecked") public Error map(Function mapper) { return (Error) this; } @Override @SuppressWarnings("unchecked") public CompletableFuture> mapResultFuture(Function>> mapper) { CompletableFuture> future = new CompletableFuture<>(); future.completeExceptionally(status.getCause()); return future; } @Override public CompletableFuture mapStatusFuture(Function> mapper) { return CompletableFuture.completedFuture(status); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Error error = (Error) o; return Objects.equals(message, error.message) && Objects.equals(status, error.status); } @Override public int hashCode() { return Objects.hash(message, status); } @Override public String toString() { return "Error{message='" + message + "', cause=" + status.getCause() + '}'; } } }