tech.ydb.core.Result Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ydb-sdk-core Show documentation
Show all versions of ydb-sdk-core Show documentation
Core module of Java SDK for YDB
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() + '}';
}
}
}