package it.tdlight.tdlibsession.td; import it.tdlight.jni.TdApi; import it.tdlight.jni.TdApi.Error; import java.util.Objects; import java.util.StringJoiner; import java.util.concurrent.CompletionException; import java.util.function.Function; import org.jetbrains.annotations.NotNull; /** * Encapsulates the result of an asynchronous operation. *

* Many operations in Vert.x APIs provide results back by passing an instance of this in a {@link io.vertx.core.Handler}. *

* The result can either have failed or succeeded. *

* If it failed then the cause of the failure is available with {@link #cause}. *

* If it succeeded then the actual result is available with {@link #result} * * @author Tim Fox */ public interface TdResult { /** * The result of the operation. This will be null if the operation failed. * * @return the result or null if the operation failed. */ T result(); /** * The result of the operation. This will throw CompletionException if the operation failed. * * @return the result. */ T orElseThrow() throws CompletionException; /** * A TdApi.Error describing failure. This will be null if the operation succeeded. * * @return the cause or null if the operation succeeded. */ TdApi.Error cause(); /** * Did it succeed? * * @return true if it succeded or false otherwise */ boolean succeeded(); /** * Did it fail? * * @return true if it failed or false otherwise */ boolean failed(); /** * Apply a {@code mapper} function on this async result.

* * The {@code mapper} is called with the completed value and this mapper returns a value. This value will complete the result returned by this method call.

* * When this async result is failed, the failure will be propagated to the returned async result and the {@code mapper} will not be called. * * @param mapper the mapper function * @return the mapped async result */ default TdResult map(Function mapper) { if (mapper == null) { throw new NullPointerException(); } return new TdResult() { @Override public U result() { if (succeeded()) { return mapper.apply(TdResult.this.result()); } else { return null; } } @Override public U orElseThrow() throws CompletionException { if (succeeded()) { return mapper.apply(TdResult.this.orElseThrow()); } else { return null; } } @Override public TdApi.Error cause() { return TdResult.this.cause(); } @Override public boolean succeeded() { return TdResult.this.succeeded(); } @Override public boolean failed() { return TdResult.this.failed(); } }; } /** * Map the result of this async result to a specific {@code value}.

* * When this async result succeeds, this {@code value} will succeeed the async result returned by this method call.

* * When this async result fails, the failure will be propagated to the returned async result. * * @param value the value that eventually completes the mapped async result * @return the mapped async result */ default TdResult map(V value) { return map((Function) t -> value); } /** * Map the result of this async result to {@code null}.

* * This is a convenience for {@code TdResult.map((T) null)} or {@code TdResult.map((Void) null)}.

* * When this async result succeeds, {@code null} will succeeed the async result returned by this method call.

* * When this async result fails, the failure will be propagated to the returned async result. * * @return the mapped async result */ default TdResult mapEmpty() { return map((V)null); } /** * Apply a {@code mapper} function on this async result.

* * The {@code mapper} is called with the failure and this mapper returns a value. This value will complete the result returned by this method call.

* * When this async result is succeeded, the value will be propagated to the returned async result and the {@code mapper} will not be called. * * @param mapper the mapper function * @return the mapped async result */ default TdResult otherwise(Function mapper) { if (mapper == null) { throw new NullPointerException(); } return new TdResult() { @Override public T result() { if (TdResult.this.succeeded()) { return TdResult.this.result(); } else if (TdResult.this.failed()) { return mapper.apply(TdResult.this.cause()); } else { return null; } } @Override public T orElseThrow() { if (TdResult.this.succeeded()) { return TdResult.this.orElseThrow(); } else if (TdResult.this.failed()) { return mapper.apply(TdResult.this.cause()); } else { return null; } } @Override public TdApi.Error cause() { return null; } @Override public boolean succeeded() { return TdResult.this.succeeded() || TdResult.this.failed(); } @Override public boolean failed() { return false; } }; } /** * Map the failure of this async result to a specific {@code value}.

* * When this async result fails, this {@code value} will succeeed the async result returned by this method call.

* * When this async succeeds, the result will be propagated to the returned async result. * * @param value the value that eventually completes the mapped async result * @return the mapped async result */ default TdResult otherwise(T value) { return otherwise(err -> value); } /** * Map the failure of this async result to {@code null}.

* * This is a convenience for {@code TdResult.otherwise((T) null)}.

* * When this async result fails, the {@code null} will succeeed the async result returned by this method call.

* * When this async succeeds, the result will be propagated to the returned async result. * * @return the mapped async result */ default TdResult otherwiseEmpty() { return otherwise(err -> null); } static TdResult succeeded(@NotNull T value) { Objects.requireNonNull(value); return new TdResultImpl(value, null); } static TdResult failed(@NotNull TdApi.Error error) { Objects.requireNonNull(error); return new TdResultImpl(null, error); } static TdResult of(@NotNull TdApi.Object resultOrError) { if (resultOrError.getConstructor() == TdApi.Error.CONSTRUCTOR) { return failed((TdApi.Error) resultOrError); } else { //noinspection unchecked return succeeded((T) resultOrError); } } class TdResultImpl implements TdResult { private final U value; private final Error error; public TdResultImpl(U value, Error error) { this.value = value; this.error = error; assert (value == null) != (error == null); } @Override public U result() { return value; } @Override public U orElseThrow() { if (error != null) { throw new TdError(error.code, error.message); } return value; } @Override public Error cause() { return error; } @Override public boolean succeeded() { return value != null; } @Override public boolean failed() { return error != null; } @Override public String toString() { return new StringJoiner(", ", TdResultImpl.class.getSimpleName() + "[", "]") .add("value=" + value) .add("error=" + error) .toString(); } } }