fi.jubic.easyconfig.internal.Result Maven / Gradle / Ivy
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());
}
}
}