package ru.yandex.qe.util;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author rurikk
 */
public class Throwables {
    /**
     * <i>Casts</i> {@link Throwing.Function Function with exception} to plain {@link java.util.function.Function}.
     * <p>
     * Useful for wrapping throwing lambdas in java8 streams:
     * <pre>
     * Arrays.asList("one", "two", "three")
     *   .stream()
     *   .map(rethrowing(s->s.getBytes("UTF-8")))
     *   ...
     * </pre>
     * <p>
     */
    public static <T, R> Function<T, R> rethrowing(Throwing.Function<T, R> function) {
        return t -> {
            try {
                return function.apply(t);
            } catch (Throwable e) {
                throw rethrow(e);
            }
        };
    }

    /**
     * Similar to {@link Throwables#rethrowing(Throwing.Function)}
     */
    public static Runnable rethrowing(Throwing.Runnable runnable) {
        return () -> {
            try {
                runnable.run();
            } catch (Throwable t) {
                throw rethrow(t);
            }
        };
    }

    /**
     * Similar to {@link Throwables#rethrowing(Throwing.Function)}
     */
    public static <T> Supplier<T> rethrowing(Throwing.Supplier<T> supplier) {
        return () -> {
            try {
                return supplier.get();
            } catch (Throwable t) {
                throw rethrow(t);
            }
        };
    }

    /**
     * Similar to {@link Throwables#rethrowing(Throwing.Function)}
     */
    public static <T> Consumer<T> rethrowing(Throwing.Consumer<T> consumer) {
        return a -> {
            try {
                consumer.accept(a);
            } catch (Throwable t) {
                throw rethrow(t);
            }
        };
    }


    /**
     * Exception wrapper which makes any exception to be {@link RuntimeException}.
     * Also known as <a href="http://projectlombok.org/features/SneakyThrows.html">Sneaky Throw</a>
     * Unlike {@link com.google.common.base.Throwables#propagate(java.lang.Throwable) Guava's propagate}
     * it doesn't add any additional exception wrappers to the trace.
     * <p>
     * <pre>
     *   try {
     *     someMethodThatCouldThrowAnything();
     *   } catch (Throwable t) {
     *     throw Throwables.rethrow(t);
     *   }
     * </pre>
     * <p>
     * Note, <b>throw</b> in the example above doesn't throw,
     * it's here for compiler to know the end of the execution path.
     */
    public static RuntimeException rethrow(Throwable ex) throws RuntimeException {
        Throwables.<RuntimeException>uncheckedThrow(ex);
        throw new IllegalStateException("never happens");
    }

    /**
     * The sneaky part of sneaky throw, relying on generics
     * limitations to evade compiler complaints about rethrowing
     * unchecked exceptions
     */
    @SuppressWarnings("unchecked")
    private static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
        throw (T) t; // rely on vacuous cast
    }
}
