From b6869c1b17c65594f65c3ad5e53461082e5c3088 Mon Sep 17 00:00:00 2001
From: pacien
Date: Fri, 29 Mar 2019 01:31:15 +0100
Subject: initial impl
---
build.gradle | 35 +++++-
settings.gradle | 19 ++-
.../java/org/pacien/lemonad/attempt/Attempt.java | 129 +++++++++++++++++++++
.../java/org/pacien/lemonad/attempt/Failure.java | 42 +++++++
.../java/org/pacien/lemonad/attempt/Success.java | 42 +++++++
.../pacien/lemonad/attempt/ThrowingSupplier.java | 32 +++++
.../lemonad/validation/ValidationResult.java | 129 +++++++++++++++++++++
.../validation/ValidationResultContainer.java | 40 +++++++
.../org/pacien/lemonad/validation/Validator.java | 80 +++++++++++++
.../org/pacien/lemonad/attempt/AttemptTest.java | 114 ++++++++++++++++++
.../lemonad/validation/ValidationResultTest.java | 75 ++++++++++++
.../pacien/lemonad/validation/ValidatorTest.java | 61 ++++++++++
12 files changed, 793 insertions(+), 5 deletions(-)
create mode 100644 src/main/java/org/pacien/lemonad/attempt/Attempt.java
create mode 100644 src/main/java/org/pacien/lemonad/attempt/Failure.java
create mode 100644 src/main/java/org/pacien/lemonad/attempt/Success.java
create mode 100644 src/main/java/org/pacien/lemonad/attempt/ThrowingSupplier.java
create mode 100644 src/main/java/org/pacien/lemonad/validation/ValidationResult.java
create mode 100644 src/main/java/org/pacien/lemonad/validation/ValidationResultContainer.java
create mode 100644 src/main/java/org/pacien/lemonad/validation/Validator.java
create mode 100644 src/test/java/org/pacien/lemonad/attempt/AttemptTest.java
create mode 100644 src/test/java/org/pacien/lemonad/validation/ValidationResultTest.java
create mode 100644 src/test/java/org/pacien/lemonad/validation/ValidatorTest.java
diff --git a/build.gradle b/build.gradle
index fb83c0a..b4b5242 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,16 +1,43 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
plugins {
- id 'java'
+ id 'java'
}
group 'org.pacien'
version '1.0-SNAPSHOT'
-sourceCompatibility = 1.8
+sourceCompatibility = 1.11
+
+test {
+ useJUnitPlatform()
+}
repositories {
- mavenCentral()
+ mavenCentral()
}
dependencies {
- testCompile group: 'junit', name: 'junit', version: '4.12'
+ compileOnly 'org.projectlombok:lombok:1.18.6'
+ testCompileOnly 'org.projectlombok:lombok:1.18.6'
+ annotationProcessor 'org.projectlombok:lombok:1.18.6'
+
+ testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.0'
+ testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.4.0'
}
diff --git a/settings.gradle b/settings.gradle
index cc93670..3796e5b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,19 @@
-rootProject.name = 'lemonad'
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+rootProject.name = 'lemonad'
diff --git a/src/main/java/org/pacien/lemonad/attempt/Attempt.java b/src/main/java/org/pacien/lemonad/attempt/Attempt.java
new file mode 100644
index 0000000..fc437ad
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/attempt/Attempt.java
@@ -0,0 +1,129 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.attempt;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import lombok.NonNull;
+
+/**
+ * Wraps either a value from a success or an error from a failure.
+ *
+ * @param the potential wrapped result type.
+ * @param the potential error type.
+ * @author pacien
+ */
+public interface Attempt {
+ /**
+ * @return whether the {@link Attempt} is successful.
+ */
+ boolean isSuccess();
+
+ /**
+ * @return whether the {@link Attempt} is failed.
+ */
+ boolean isFailure();
+
+ /**
+ * @return the result if this {@link Attempt} is a success.
+ * @throws java.util.NoSuchElementException if this {@link Attempt} is a failure.
+ */
+ R getResult();
+
+ /**
+ * @return the error if this {@link Attempt} is a failure.
+ * @throws java.util.NoSuchElementException if this {@link Attempt} is a success.
+ */
+ E getError();
+
+ /**
+ * @param resultConsumer a {@link Consumer} of result called if the {@link Attempt} is a success.
+ * @return the current {@link Attempt}
+ */
+ default Attempt ifSuccess(@NonNull Consumer super R> resultConsumer) {
+ if (isSuccess()) resultConsumer.accept(getResult());
+ return this;
+ }
+
+ /**
+ * @param errorConsumer a {@link Consumer} of error called if the {@link Attempt} is a failure.
+ * @return the current {@link Attempt}
+ */
+ default Attempt ifFailure(@NonNull Consumer super E> errorConsumer) {
+ if (isFailure()) errorConsumer.accept(getError());
+ return this;
+ }
+
+ /**
+ * @param mapper a function producing an {@link Attempt}, called with the current result if this {@link Attempt} is a success.
+ * @return this {@link Attempt} if it is a failure, or the produced one otherwise.
+ */
+ default Attempt mapResult(@NonNull Function super R, ? extends Attempt extends RR, ? extends E>> mapper) {
+ //noinspection unchecked
+ return (Attempt) (isSuccess() ? mapper.apply(getResult()) : this);
+ }
+
+ /**
+ * @param mapper a function producing an {@link Attempt}, called with the current error if this {@link Attempt} is a failure.
+ * @return this {@link Attempt} if it is a success, or the alternative {@link Attempt} retrieved from the supplier otherwise.
+ */
+ default Attempt mapFailure(@NonNull Function super E, ? extends Attempt extends R, ? extends EE>> mapper) {
+ //noinspection unchecked
+ return (Attempt) (isFailure() ? mapper.apply(getError()) : this);
+ }
+
+ /**
+ * @param mapper a function transforming an {@link Attempt}.
+ * @return the transformed {@link Attempt}
+ */
+ default Attempt flatMap(@NonNull Function super Attempt super R, ? super E>, ? extends Attempt extends RR, ? extends EE>> mapper) {
+ //noinspection unchecked
+ return (Attempt) mapper.apply(this);
+ }
+
+ /**
+ * @param result the result of the {@link Attempt}.
+ * @return a successful {@link Attempt} wrapping the supplied result.
+ */
+ static Attempt success(R result) {
+ return new Success<>(result);
+ }
+
+ /**
+ * @param error the cause of the failure of the {@link Attempt}.
+ * @return a failed {@link Attempt} with the supplied error.
+ */
+ static Attempt failure(E error) {
+ return new Failure<>(error);
+ }
+
+ /**
+ * @param supplier a {@code Supplier} that may throw an {@link Throwable}.
+ * @return an {@link Attempt} wrapping either the result of the execution of the supplier or any thrown {@link Throwable}.
+ */
+ static Attempt attempt(@NonNull ThrowingSupplier extends R, ? extends E> supplier) {
+ try {
+ return success(supplier.get());
+ } catch (Throwable throwable) {
+ //noinspection unchecked
+ return (Attempt) failure(throwable);
+ }
+ }
+}
diff --git a/src/main/java/org/pacien/lemonad/attempt/Failure.java b/src/main/java/org/pacien/lemonad/attempt/Failure.java
new file mode 100644
index 0000000..752e981
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/attempt/Failure.java
@@ -0,0 +1,42 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.attempt;
+
+import java.util.NoSuchElementException;
+
+import lombok.Value;
+
+/**
+ * @author pacien
+ */
+@Value class Failure implements Attempt {
+ E error;
+
+ @Override public boolean isSuccess() {
+ return false;
+ }
+
+ @Override public boolean isFailure() {
+ return true;
+ }
+
+ @Override public R getResult() {
+ throw new NoSuchElementException();
+ }
+}
diff --git a/src/main/java/org/pacien/lemonad/attempt/Success.java b/src/main/java/org/pacien/lemonad/attempt/Success.java
new file mode 100644
index 0000000..928fbb1
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/attempt/Success.java
@@ -0,0 +1,42 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.attempt;
+
+import java.util.NoSuchElementException;
+
+import lombok.Value;
+
+/**
+ * @author pacien
+ */
+@Value class Success implements Attempt {
+ R result;
+
+ @Override public boolean isSuccess() {
+ return true;
+ }
+
+ @Override public boolean isFailure() {
+ return false;
+ }
+
+ @Override public E getError() {
+ throw new NoSuchElementException();
+ }
+}
diff --git a/src/main/java/org/pacien/lemonad/attempt/ThrowingSupplier.java b/src/main/java/org/pacien/lemonad/attempt/ThrowingSupplier.java
new file mode 100644
index 0000000..6dc7854
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/attempt/ThrowingSupplier.java
@@ -0,0 +1,32 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.attempt;
+
+/**
+ * @param the result type.
+ * @param the {@link Throwable} type.
+ * @author pacien
+ */
+public interface ThrowingSupplier {
+ /**
+ * @return a result.
+ * @throws T a potential {@link Throwable}.
+ */
+ R get() throws T;
+}
diff --git a/src/main/java/org/pacien/lemonad/validation/ValidationResult.java b/src/main/java/org/pacien/lemonad/validation/ValidationResult.java
new file mode 100644
index 0000000..65bb389
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/validation/ValidationResult.java
@@ -0,0 +1,129 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.validation;
+
+import org.pacien.lemonad.attempt.Attempt;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import lombok.NonNull;
+
+import static java.util.stream.Collectors.toUnmodifiableList;
+import static org.pacien.lemonad.attempt.Attempt.failure;
+import static org.pacien.lemonad.attempt.Attempt.success;
+
+/**
+ * Wraps the result of the validation of a subject.
+ *
+ * @param the subject type,
+ * @param the error type.
+ * @author pacien
+ */
+public interface ValidationResult {
+ /**
+ * @return whether no error have been reported during the validation.
+ */
+ boolean isValid();
+
+ /**
+ * @return whether some error have been reported during the validation.
+ */
+ boolean isInvalid();
+
+ /**
+ * @return the subject of the validation.
+ */
+ S getSubject();
+
+ /**
+ * @return the potentially empty list of reported validation errors.
+ */
+ List getErrors();
+
+ /**
+ * @param consumer a subject consumer called if the validation is successful.
+ * @return the current object.
+ */
+ default ValidationResult ifValid(@NonNull Consumer super S> consumer) {
+ if (isValid()) consumer.accept(getSubject());
+ return this;
+ }
+
+ /**
+ * @param consumer the consumer called with the validation subject and reported errors if the validation is failed.
+ * @return the current object.
+ */
+ default ValidationResult ifInvalid(@NonNull BiConsumer super S, ? super List super E>> consumer) {
+ if (!isValid()) consumer.accept(getSubject(), getErrors());
+ return this;
+ }
+
+ /**
+ * @return an {@link Attempt} with a state corresponding to the one of the validation.
+ */
+ default Attempt> toAttempt() {
+ return isValid() ? success(getSubject()) : failure(getErrors());
+ }
+
+ /**
+ * @param mapper a function transforming a {@link ValidationResult}.
+ * @return the transformed {@link ValidationResult}.
+ */
+ default ValidationResult flatMap(@NonNull Function super ValidationResult super S, ? super E>, ? extends ValidationResult extends SS, ? extends EE>> mapper) {
+ //noinspection unchecked
+ return (ValidationResult) mapper.apply(this);
+ }
+
+ /**
+ * @param subject an overriding subject.
+ * @param validationResults a {@link Stream} of {@link ValidationResult}s to merge.
+ * @return the merged {@link ValidationResult} containing all errors from the supplied ones.
+ */
+ static ValidationResult merge(S subject, @NonNull Stream extends ValidationResult, ? extends E>> validationResults) {
+ return new ValidationResultContainer<>(
+ subject,
+ validationResults.flatMap(res -> res.getErrors().stream()).collect(toUnmodifiableList()));
+ }
+
+ /**
+ * @param subject the suject of the validation.
+ * @return a successful {@link ValidationResult}.
+ */
+ static ValidationResult valid(S subject) {
+ return new ValidationResultContainer<>(subject, List.of());
+ }
+
+ /**
+ * @param subject the suject of the validation.
+ * @param error a validation error.
+ * @param errors additional validation errors.
+ * @return a failed {@link ValidationResult} for the supplied subject.
+ */
+ @SafeVarargs static ValidationResult invalid(S subject, E error, E... errors) {
+ return new ValidationResultContainer<>(
+ subject,
+ Stream.concat(Stream.of(error), Arrays.stream(errors)).map(Objects::requireNonNull).collect(toUnmodifiableList()));
+ }
+}
diff --git a/src/main/java/org/pacien/lemonad/validation/ValidationResultContainer.java b/src/main/java/org/pacien/lemonad/validation/ValidationResultContainer.java
new file mode 100644
index 0000000..2c752f6
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/validation/ValidationResultContainer.java
@@ -0,0 +1,40 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.validation;
+
+import java.util.List;
+
+import lombok.NonNull;
+import lombok.Value;
+
+/**
+ * @author pacien
+ */
+@Value class ValidationResultContainer implements ValidationResult {
+ S subject;
+ @NonNull List errors;
+
+ @Override public boolean isValid() {
+ return errors.isEmpty();
+ }
+
+ @Override public boolean isInvalid() {
+ return !isValid();
+ }
+}
diff --git a/src/main/java/org/pacien/lemonad/validation/Validator.java b/src/main/java/org/pacien/lemonad/validation/Validator.java
new file mode 100644
index 0000000..a8a9e3f
--- /dev/null
+++ b/src/main/java/org/pacien/lemonad/validation/Validator.java
@@ -0,0 +1,80 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.validation;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import lombok.NonNull;
+import lombok.val;
+
+import static java.util.stream.Collectors.toUnmodifiableList;
+import static org.pacien.lemonad.validation.ValidationResult.invalid;
+import static org.pacien.lemonad.validation.ValidationResult.valid;
+
+/**
+ * A function which applies validation rules on a subject and reports possible errors.
+ *
+ * @param the subject type
+ * @param the error type
+ * @author pacien
+ */
+@FunctionalInterface public interface Validator {
+ /**
+ * @param subject the subject to validate, which can potentially be null.
+ * @return the non-null result of the validation of the supplied subject.
+ */
+ ValidationResult validate(S subject);
+
+ /**
+ * @param predicate the validation predicate testing the validity of a subject.
+ * @param negativeError an error to return if the subject does not pass the test.
+ * @return a {@link Validator} based on the supplied predicate and error.
+ */
+ static Validator ensuringPredicate(@NonNull Predicate super S> predicate, @NonNull E negativeError) {
+ return subject -> predicate.test(subject) ? valid(subject) : invalid(subject, negativeError);
+ }
+
+ /**
+ * @param validators the {@link Validator}s to combine, to be evaluated in order of listing.
+ * @return a {@link Validator} based on the supplied ones.
+ */
+ @SafeVarargs static Validator validatingAll(@NonNull Validator super S, ? extends E>... validators) {
+ val validatorList = Arrays.stream(validators).map(Objects::requireNonNull).collect(toUnmodifiableList());
+ return subject -> new ValidationResultContainer<>(
+ subject,
+ validatorList.stream()
+ .flatMap(validator -> validator.validate(subject).getErrors().stream())
+ .collect(toUnmodifiableList()));
+ }
+
+ /**
+ * @param getter the field getter mapping the validation subject.
+ * @param validator the {@link Validator} validating the field.
+ * @return a {@link Validator} validating the parent object.
+ */
+ static Validator validatingField(@NonNull Function super S, ? extends F> getter,
+ @NonNull Validator super F, ? extends E> validator) {
+ //noinspection unchecked
+ return subject -> new ValidationResultContainer<>(subject, (List) validator.validate(getter.apply(subject)).getErrors());
+ }
+}
diff --git a/src/test/java/org/pacien/lemonad/attempt/AttemptTest.java b/src/test/java/org/pacien/lemonad/attempt/AttemptTest.java
new file mode 100644
index 0000000..1b165e8
--- /dev/null
+++ b/src/test/java/org/pacien/lemonad/attempt/AttemptTest.java
@@ -0,0 +1,114 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.attempt;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.NoSuchElementException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author pacien
+ */
+class AttemptTest {
+ @Test void testSimpleSuccess() {
+ var result = "result";
+ var success = Attempt.success(result);
+ assertFalse(success.isFailure());
+ assertTrue(success.isSuccess());
+ assertThrows(NoSuchElementException.class, success::getError);
+ assertEquals(result, success.getResult());
+ success.ifFailure(__ -> fail());
+ success.ifSuccess(innerResult -> assertEquals(result, innerResult));
+ }
+
+ @Test void testSimpleFailure() {
+ var fault = 0;
+ var failure = Attempt.failure(fault);
+ assertTrue(failure.isFailure());
+ assertFalse(failure.isSuccess());
+ assertEquals(fault, failure.getError());
+ assertThrows(NoSuchElementException.class, failure::getResult);
+ failure.ifFailure(innerFault -> assertEquals(fault, innerFault));
+ failure.ifSuccess(__ -> fail());
+ }
+
+ @Test void testNormalAttempt() {
+ var result = "result";
+ var success = Attempt.attempt(() -> result);
+ assertFalse(success.isFailure());
+ assertTrue(success.isSuccess());
+ assertThrows(NoSuchElementException.class, success::getError);
+ assertEquals(result, success.getResult());
+ success.ifFailure(__ -> fail());
+ success.ifSuccess(innerResult -> assertEquals(result, innerResult));
+ }
+
+ @Test void testFailedAttempt() {
+ var exception = new Exception();
+ var failure = Attempt.attempt(() -> {
+ throw exception;
+ });
+ assertTrue(failure.isFailure());
+ assertFalse(failure.isSuccess());
+ assertEquals(exception, failure.getError());
+ assertThrows(NoSuchElementException.class, failure::getResult);
+ failure.ifFailure(innerFault -> assertEquals(exception, innerFault));
+ failure.ifSuccess(__ -> fail());
+ }
+
+ @Test void testTransformationFlow() {
+ var result0 = 0;
+ var result1 = "res";
+ var result2 = 0L;
+ var fault0 = 0;
+ var fault1 = 1;
+ var fault2 = 2;
+
+ Attempt.success(result0)
+ .mapFailure(__ -> fail())
+ .mapResult(res -> Attempt.success(result1))
+ .mapResult(res -> {
+ assertEquals(result1, res);
+ return Attempt.failure(fault0);
+ })
+ .ifSuccess(__ -> fail())
+ .mapResult(__ -> fail())
+ .mapFailure(f -> {
+ assertEquals(fault0, f);
+ return Attempt.failure(fault1);
+ })
+ .mapFailure(f -> {
+ assertEquals(fault1, f);
+ return Attempt.success(result2);
+ })
+ .ifFailure(__ -> fail())
+ .flatMap(attempt -> {
+ assertEquals(result2, attempt.getResult());
+ return Attempt.failure(fault2);
+ })
+ .ifSuccess(__ -> fail())
+ .ifFailure(f -> assertEquals(fault2, f));
+ }
+}
diff --git a/src/test/java/org/pacien/lemonad/validation/ValidationResultTest.java b/src/test/java/org/pacien/lemonad/validation/ValidationResultTest.java
new file mode 100644
index 0000000..65b2cb3
--- /dev/null
+++ b/src/test/java/org/pacien/lemonad/validation/ValidationResultTest.java
@@ -0,0 +1,75 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.validation;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author pacien
+ */
+class ValidationResultTest {
+ @Test void testValidResult() {
+ var subject = "subject";
+ var validationResult = ValidationResult.valid(subject);
+ assertTrue(validationResult.getErrors().isEmpty());
+ assertTrue(validationResult.isValid());
+ assertFalse(validationResult.isInvalid());
+ validationResult.ifValid(innerSubject -> assertEquals(subject, innerSubject));
+ validationResult.ifInvalid((__, ___) -> fail());
+ }
+
+ @Test void testInvalidResult() {
+ var subject = "subject";
+ var errors = List.of(0, 1);
+ var validationResult = ValidationResult.invalid(subject, 0, 1);
+ assertEquals(errors, validationResult.getErrors());
+ assertFalse(validationResult.isValid());
+ assertTrue(validationResult.isInvalid());
+ validationResult.ifValid(Assertions::fail);
+ validationResult.ifInvalid((innerSubject, innerErrors) -> {
+ assertEquals(subject, innerSubject);
+ assertEquals(errors, innerErrors);
+ });
+ }
+
+ @Test void testFlatMap() {
+ ValidationResult.valid("subject")
+ .ifInvalid((__, ___) -> fail())
+ .flatMap(res -> ValidationResult.invalid(res.getSubject(), 0))
+ .ifValid(innerSubject -> fail());
+ }
+
+ @Test void testMerge() {
+ var subject = "subject";
+ assertEquals(List.of(0, 1, 2, 3), ValidationResult.merge(subject, Stream.of(
+ ValidationResult.valid(subject),
+ ValidationResult.invalid(subject, 0, 1),
+ ValidationResult.invalid(subject, 2, 3))
+ ).getErrors());
+ }
+}
diff --git a/src/test/java/org/pacien/lemonad/validation/ValidatorTest.java b/src/test/java/org/pacien/lemonad/validation/ValidatorTest.java
new file mode 100644
index 0000000..55927b5
--- /dev/null
+++ b/src/test/java/org/pacien/lemonad/validation/ValidatorTest.java
@@ -0,0 +1,61 @@
+/*
+ * lemonad - Some functional sweetness for Java
+ * Copyright (C) 2019 Pacien TRAN-GIRARD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.pacien.lemonad.validation;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static java.util.function.Predicate.not;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author pacien
+ */
+class ValidatorTest {
+ @Test void testValidatorEnsuringPredicate() {
+ var emptyError = 0;
+ var validator = Validator.ensuringPredicate(not(String::isEmpty), emptyError);
+ assertEquals(List.of(emptyError), validator.validate("").getErrors());
+ assertEquals(List.of(), validator.validate("test").getErrors());
+ }
+
+ @Test void testValidatorValidatingAll() {
+ var emptyError = 0;
+ var tooLongError = 1;
+ var containsBadLetterError = 2;
+
+ var validator = Validator.validatingAll(
+ Validator.ensuringPredicate(not(String::isEmpty), emptyError),
+ Validator.ensuringPredicate((String str) -> str.length() < 10, tooLongError),
+ Validator.ensuringPredicate((String str) -> !str.contains("e"), containsBadLetterError));
+
+ assertEquals(List.of(emptyError), validator.validate("").getErrors());
+ assertEquals(List.of(tooLongError, containsBadLetterError), validator.validate("test test test").getErrors());
+ assertEquals(List.of(), validator.validate("potato").getErrors());
+ }
+
+ @Test void testValidatingField() {
+ var emptyError = 0;
+ var fieldValidator = Validator.ensuringPredicate((Integer len) -> len > 0, emptyError);
+ var validator = Validator.validatingField(String::length, fieldValidator);
+ assertEquals(List.of(emptyError), validator.validate("").getErrors());
+ assertEquals(List.of(), validator.validate("test").getErrors());
+ }
+}
--
cgit v1.2.3