/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.functional.checked;

import net.ranides.assira.trace.ExceptionUtils;

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

/**
 * This functional inferface represents {@link Function} which could throw checked exception
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <T> T
 * @param <R> R
 * @param <E> E
 */
public interface CheckedFunction<T,R,E extends Exception> {

    /**
     * Performs operation on given argument.
     *
     * @param value value
     * @return result
     * @throws E on error
     */
    R apply(T value) throws E;

    /**
     * Performs operation on given argument.
     * Usafe method: it rethrows checked exceptions, but without declaring them.
     *
     * Please note, that javac does not allow to catch checked exception of specific type if they are not declared.
     * In such situations you can only catch very general "Exception" type.
     *
     * @param value value
     * @return result
     */
    default R $apply(T value) {
        try {
            return apply(value);
        } catch(Exception cause) {
            throw ExceptionUtils.rethrow(cause);
        }
    }

    /**
     * Converts this function into sneaky version, which rethrows checked exception, but without declaring it.
     * Please note, that code expecting ordinary Function is not prerared for handling checked throws
     * and could work incorrectly when exception happens.
     *
     * @param function function
     * @param <T> T
     * @param <R> R
     * @param <E> E
     * @return Function
     */
    static  <T, R, E extends Exception> Function<T,R> sneaky(CheckedFunction<T, R, E> function) {
        return function::$apply;
    }

}
