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

fi.jubic.easyconfig.internal.Result Maven / Gradle / Ivy

There is a newer version: 0.10.4
Show newest version
package fi.jubic.easyconfig.internal;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.annotation.Nullable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class Result {
    private final T value;
    private final List messages;

    private Result(
            T value,
            List messages
    ) {
        if (value != null && !messages.isEmpty()) {
            throw new IllegalStateException();
        }
        this.value = value;
        this.messages = Collections.unmodifiableList(messages);
    }

    public static  Result of(T value) {
        return new Result<>(value, Collections.emptyList());
    }

    public static  Result message(String message) {
        return new Result<>(null, Collections.singletonList(new Message(message, null)));
    }

    public static  Result message(String message, Throwable throwable) {
        return new Result<>(null, Collections.singletonList(new Message(message, throwable)));
    }

    public static  Result message(List messages) {
        return new Result<>(null, messages);
    }

    public T getValue() {
        return value;
    }

    public List getMessages() {
        return Collections.unmodifiableList(messages);
    }

    public boolean hasMessages() {
        return !messages.isEmpty();
    }

    public  Result map(Function mapper) {
        if (!messages.isEmpty()) {
            return Result.message(messages);
        }
        return Result.of(mapper.apply(value));
    }

    public  Result flatMap(Function> mapper) {
        if (!messages.isEmpty()) {
            return Result.message(messages);
        }
        return mapper.apply(value);
    }

    public Stream getMessagesAsStringStream() {
        return messages.stream().flatMap(Message::toStringStream);
    }

    public static  Result> unwrap(List> results) {
        if (results.stream().anyMatch(Result::hasMessages)) {
            return results.stream()
                    .map(Result::getMessages)
                    .flatMap(List::stream)
                    .collect(
                            Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    Result::message
                            )
                    );
        }

        return results.stream()
                .map(Result::getValue)
                .collect(
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                Result::of
                        )
                );
    }

    public static Result> unsafeUnwrap(List> results) {
        if (results.stream().anyMatch(Result::hasMessages)) {
            return results.stream()
                    .map(Result::getMessages)
                    .flatMap(List::stream)
                    .collect(
                            Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    Result::message
                            )
                    );
        }

        return results.stream()
                .map(Result::getValue)
                .collect(
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                Result::of
                        )
                );
    }

    public static  Result unwrapMessages(List> results) {
        return results.stream()
                .peek(result -> {
                    if (!result.hasMessages()) {
                        throw new IllegalArgumentException(
                                "Result has no messages"
                        );
                    }
                })
                .map(Result::getMessages)
                .flatMap(List::stream)
                .collect(
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                Result::message
                        )
                );
    }

    public static  Result unwrapMessages(Result... results) {
        return unwrapMessages(Arrays.asList(results));
    }

    @SuppressFBWarnings(
            value = "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE",
            justification = "The stacktraces are printed to messages of other throwables "
                    + "making this equivalent to Throwable::printStackTrace()"
    )
    static class Message {
        private final String text;
        @Nullable
        private final Throwable throwable;

        Message(String text, @Nullable Throwable throwable) {
            this.text = text;
            this.throwable = throwable;
        }

        Optional getThrowable() {
            return Optional.ofNullable(throwable);
        }

        Stream toStringStream() {
            return Stream
                    .of(
                            Stream.of(text),
                            getThrowable()
                                    .map(t -> {
                                        StringWriter writer = new StringWriter();
                                        t.printStackTrace(new PrintWriter(writer));
                                        return Stream.of(writer.toString()
                                                .split(System.lineSeparator()))
                                                .map(row -> "    " + row);
                                    })
                                    .orElseGet(Stream::empty)
                    )
                    .flatMap(Function.identity());
        }
    }
}