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

com.power4j.tile.result.Result Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.power4j.tile.result;

import com.power4j.tile.fmt.Display;
import org.springframework.lang.Nullable;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Similar to Rust's {@code Result} type ,A single Result value can only encapsulate
 * either a value of type T or a value of type E.
* It is usually used to indicate success or error * * @author CJ ([email protected]) * @since 1.0 */ public class Result implements Display { private final static Result EMPTY = new Result<>(new Object(), null); @Nullable private final T value; @Nullable private final E error; /** * Provide a value * @param value The value, can not be null,Use {@code Optional#ofNullable} to wrap * null * @return New Result object * @param Type parameter of value * @throws NullPointerException if value is null */ public static Result ok(T value) { return new Result<>(Objects.requireNonNull(value), null); } /** * Provide a value,Use {@code Optional#ofNullable} to wrap nullable value * @param value The value null * @return New Result object * @param Type parameter of value * @throws NullPointerException if value is null */ public static Result, E> some(@Nullable T value) { return new Result<>(Optional.ofNullable(value), null); } /** * Success but no value * @return New Result object * @throws NullPointerException if value is null */ @SuppressWarnings("unchecked") public static Result none() { return (Result) EMPTY; } /** * Provide an error * @param err The error object, can not be null * @return New Result object * @param Type parameter of error * @throws NullPointerException if err is null */ public static Result error(E err) { return new Result<>(null, Objects.requireNonNull(err)); } Result(@Nullable T value, @Nullable E error) { this.value = value; this.error = error; } /** * Create a copy of this * @return new {@code Result} */ public Result copy() { return new Result<>(value, error); } /** * Test if it is an error
	 *     Result.error(1).isError()    -> true
	 *     Result.ok(null).isError()    -> false
	 * 
* @return true if it is an error */ public boolean isError() { return error != null; } /** * Invoke the predicate on the error if it is an error
	 *     Result.error(-1).isErrorAnd(err -> err == -1)   -> true
	 *     Result.error(-1).isErrorAnd(err -> err == 0 )   -> false
	 *     Result.ok(null).isErrorAnd(err -> true)         -> false, predicate not used
	 * 
* @param predicate Predicate function * @return true means it is an error and the predicate is true */ public boolean isErrorAnd(Predicate predicate) { return isError() && predicate.test(error); } /** * Test if it is NOT an error
	 *     Result.error(1).isOk()       -> false
	 *     Result.ok(null).isOk()       -> true
	 *     Result.ok(1).isOk()          -> true
	 * 
* @return true means it is not an error */ public boolean isOk() { return !isError(); } /** * Invoke the predicate on the value if it is not an error
	 *     Result.ok(-1).isOkAnd(val -> val == -1)       -> true
	 *     Result.ok(-1).isOkAnd(val -> val == 0 )       -> false
	 *     Result.error(-1).isOkAnd(val -> true)         -> false, predicate not used
	 * 
* @param predicate Predicate function * @return true means it is not an error and the predicate is true for the value */ public boolean isOkAnd(Predicate predicate) { return isOk() && predicate.test(value); } /** * Unpack the value
	 *     Result.ok(-1).unwrap()         -> -1
	 *     Result.ok(null).unwrap()       -> null
	 *     Result.error(-1).unwrap()      -> IllegalResultException
	 * 
* @return The value, never null * @throws IllegalStateException could not provide value because it is an error * @see Result#isError() * @see Result#unwrapOrElse(Supplier) */ public T unwrap() { if (isError()) { assert error != null; String info = error instanceof Display ? ((Display) error).display() : error.toString(); throw new IllegalStateException("Result is error:" + info); } assert value != null; return value; } /** * Unpack the value,throw exception if it is an error
	 *     Result.ok(1).unwrapOrRise(() -> new RuntimeException())             -> 1
	 *     Result.error(-1).unwrapOrRise(() -> new RuntimeException())         -> RuntimeException
	 * 
* @param func Convert function, used to convert error to exception * @return Unpacked value */ public T unwrapOrThrow(Function func) { if (isError()) { assert error != null; throw func.apply(error); } assert value != null; return value; } /** * Unpack the value,fallback to default value if it is an error
	 *     Result.ok(1).unwrapOr(2)             -> 1
	 *     Result.error(-1).unwrapOr(2)         -> 2
	 * 
* @param defVal the default value, can not be null,Use {@code Optional#ofNullable} to * wrap null * @return Unpacked value or default value * @see Result#isError() */ public T unwrapOr(T defVal) { if (isError()) { return Objects.requireNonNull(defVal); } assert value != null; return value; } /** * Similar to {@link #unwrapOr(T)},but use supplier instead
	 *     Result.ok(1).unwrapOr(() -> 2)             -> 1
	 *     Result.error(-1).unwrapOr(() -> 2)         -> 2
	 * 
* @param supplier the supplier, used if it is an error,The supplier returns the null * value is not allowed. * @return Unpacked value or returned from supplier */ public T unwrapOrElse(Supplier supplier) { if (isError()) { return Objects.requireNonNull(supplier.get()); } assert value != null; return value; } /** * Unpack the error object
	 *     Result.error(-1).unwrapError()         -> -1
	 *     Result.ok(null).unwrapError()          -> IllegalResultException
	 * 
* @return E * @throws IllegalStateException could not provide error because it is not an error * @see Result#tryUnwrapError() */ public E unwrapError() { return tryUnwrapError().orElseThrow(() -> new IllegalStateException("not an error")); } /** * Unpack the error object and cast it to clazz
	 * @return E
	 * @throws IllegalStateException could not provide error because it is not an error
	 * @throws ClassCastException if the object is not null and is not assignable to the
	 * type F.
	 * @see Result#tryUnwrapError()
	 */
	public  F unwrapError(Class clazz) {
		E err = unwrapError();
		return clazz.cast(err);
	}

	/**
	 * Try to unpack the error object 
	 *     Result.error(-1).tryUnwrapError()         -> Optional(-1)
	 *     Result.ok(null).tryUnwrapError()          -> Optional(empty)
	 * 
* @return Optional if it is an error, Optional.empty() otherwise * @see Result#isError() * */ public Optional tryUnwrapError() { if (isError()) { assert error != null; return Optional.of(error); } return Optional.empty(); } /** * Value convert, Convert {@code Result} to {@code Result}
	 *     Result.ok(1).map(val -> "val "+ val)         -> Result.ok("val 1")
	 *     Result.error(1).map(val -> "val "+ val)      -> Result.error(1)
	 * 
* @param func convert function,used if it is not an error * @return New {@code Result} object * @param Type parameter of new value */ public Result map(Function func) { if (isOk()) { return Result.ok(func.apply(value)); } assert error != null; return Result.error(error); } /** * Error convert, Convert {@code Result} to {@code Result}
	 *     Result.error(1).mapError(err -> "error "+ err)      -> Result.error("error 1")
	 *     Result.ok(1).mapError(err -> "error "+ err)         -> Result.ok(1)
	 * 
* @param func convert function,used if it is an error.Provide null is not allowed * @return New {@code Result} object * @param Type parameter of new error */ public Result mapError(Function func) { if (isOk()) { assert value != null; return Result.ok(value); } return Result.error(func.apply(error)); } /** * Value convert or fallback, Convert {@code Result} to {@code Result} if it * is NOT an error,Otherwise fallback to default value
	 *     Result.ok(1).mapOr(val -> val + 1,-1)           -> 2
	 *     Result.ok(1).mapOr(val -> val + 10,-1)          -> 11
	 *     Result.error(1).mapOr(val -> val + 1,-1)        -> -1 ,fallback to default value
	 * 
* @param func Convert function,used if it is not an error.Return null is not allowed * @param defVal default value,used if it is an error.Null is not allowed * @return Converted value or default value * */ public U mapOr(Function func, U defVal) { if (isOk()) { return Objects.requireNonNull(func.apply(value)); } return Objects.requireNonNull(defVal); } /** * Similar to {@link Result#mapOr(Function, Object)},the default value is provided by * supplier
	 *     Result.ok(1).mapOr(val -> val + 1, () -> -1)          -> 2
	 *     Result.ok(1).mapOr(val -> val + 10,() -> -1)          -> 11
	 *     Result.error(1).mapOr(val -> val + 1,() -> -1)        -> -1 ,fallback to supplier
	 * 
* @param func Convert function,used if it is not an error.Return null is not allowed * @param supplier default value provider,used if it is an error.Return null is not * allowed * @return Converted value or default value * @see Result#isError() * */ public U mapOrElse(Function func, Supplier supplier) { if (isOk()) { return Objects.requireNonNull(func.apply(value)); } return Objects.requireNonNull(supplier.get()); } /** * Logical "and" chain operation, use second if it is NOT an error
	 *     Result.ok(1).and(Result.ok("1"))           -> Result.ok("1"), use second
	 *     Result.ok(1).and(Result.error("1"))        -> Result.error("1"), use second
	 *     Result.error(1).and(Result.ok("1"))        -> Result.error(1), original error
	 *     Result.error(1).and(Result.error("1"))     -> Result.error(1) original error
	 * 
* @param other Another result object, used if it is NOT an error * @return New {@code Result} object */ public Result and(Result other) { if (isError()) { assert error != null; return Result.error(error); } else { return other; } } /** * Logical "and" chain operation, Similar to {@link Result#and(Result) }, Use compute * function to convert {@code Result} if it is NOT an error
	 *     Result.ok(1).andThen(val -> Result.ok("1"))           -> Result.ok("1")
	 *     Result.ok(1).andThen(val -> Result.error("1"))        -> Result.error("1")
	 *     Result.error(1).andThen(val -> Result.ok("1"))        -> Result.error(1), original error
	 *     Result.error(1).andThen(val -> Result.error("1"))     -> Result.error(1), original error
	 * 
* @param func Compute function * @return New {@code Result} object */ public Result andThen(Function> func) { if (isError()) { assert error != null; return Result.error(error); } else { return func.apply(value); } } /** * Logic "or" chain operation, use second if it is an error
	 *     Result.error(1).or(Result.ok("1"))           ->  Result.ok("1"), use second
	 *     Result.error(1).or(Result.error("1"))        ->  Result.error("1"), use second
	 *     Result.ok(1).or(Result.ok("1"))              ->  Result.ok(1), original ok
	 *     Result.ok(1).or(Result.error("1"))           ->  Result.ok(1) original ok
	 * 
* @param other Another result object * @return New {@code Result} object */ public Result or(Result other) { if (isError()) { return other; } else { assert value != null; return Result.ok(value); } } /** * Logic "or" chain operation, Similar to {@link Result#or(Result) }, Use compute * function to convert {@code Result} if it is an error
	 *     Result.error(1).orElse(err -> Result.ok("1"))           ->  Result.ok("1")
	 *     Result.error(1).orElse(err -> Result.error(""))         ->  Result.error("")
	 *     Result.ok(1).orElse(err -> Result.ok("1"))              ->  Result.ok(1), original ok
	 *     Result.ok(1).orElse(err -> Result.error(""))            ->  Result.ok(1) original ok
	 * 
* @param func Convert function * @return New {@code Result} object */ public Result orElse(Function> func) { if (isError()) { return func.apply(error); } else { assert value != null; return Result.ok(value); } } /** * Extract the inner Result object
	 *     Result.flatten(Result.ok(Result.ok(1)))              ->  Result.ok(1)
	 *     Result.flatten(Result.ok(Result.error(1)))           ->  Result.error(1)
	 *     Result.flatten(Result.ok(Result.ok(Result.ok(1))))   ->  Result.ok(Result.ok(1))
	 *     Result.flatten(Result.error(1))                      ->  Result.error(1)
	 * 
* @param result Nested result object * @return Inner result object * @param Type parameter of the value * @param Type parameter of the error */ public static Result flatten(Result, E> result) { return result.andThen(Function.identity()); } @Override public String display() { if (isOk()) { if (value instanceof Optional && !((Optional) value).isPresent()) { return "Ok(empty)"; } return "Ok"; } return "Error(" + error + ")"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Result result = (Result) o; return Objects.equals(value, result.value) && Objects.equals(error, result.error); } @Override public int hashCode() { return Objects.hash(value, error); } }