Skip to content

Commit e524924

Browse files
authored
Add Validation.all() to combine multiple validations. (#2735)
1 parent d164913 commit e524924

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

src/main/java/io/vavr/control/Validation.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
package io.vavr.control;
2020

2121
import io.vavr.*;
22+
import io.vavr.collection.Array;
2223
import io.vavr.collection.Iterator;
2324
import io.vavr.collection.List;
2425
import io.vavr.collection.Seq;
26+
import io.vavr.collection.Traversable;
2527

2628
import java.io.Serializable;
2729
import java.util.NoSuchElementException;
@@ -187,6 +189,52 @@ public static <E, T> Validation<Seq<E>, Seq<T>> sequence(Iterable<? extends Vali
187189
return errors.isEmpty() ? valid(list.reverse()) : invalid(errors.reverse());
188190
}
189191

192+
/**
193+
* A wrapper to {@link #all(Traversable)}.
194+
* <p/>
195+
* Usage example :
196+
*
197+
* <pre>{@code
198+
* Validation<String, Path> isDirectory(Path path){
199+
* [...]
200+
* }
201+
*
202+
* Validation<String, Path> isWritable(Path path){
203+
* [...]
204+
* }
205+
*
206+
* Validation<Seq<String>, Path> checkWritableDirectory = Validation.all(
207+
* isDirectory(path),
208+
* isWritable(path)
209+
* );
210+
* }</pre>
211+
*
212+
* @see #all(Traversable)
213+
*/
214+
@SuppressWarnings("varargs")
215+
@SafeVarargs
216+
public static <E, T> Validation<Seq<E>, T> all(final Validation <? extends E, ? extends T>... values) {
217+
Objects.requireNonNull(values, "values is null");
218+
return all(Array.of(values));
219+
}
220+
221+
/**
222+
* Combine many {@code Validation} of the same type into a single {@code Validation} with a list of invalid values.
223+
*
224+
*
225+
* @param <E> value type in the case of invalid
226+
* @param <T> value type in the case of valid
227+
* @param values An iterable of Validation instances.
228+
* @return A valid Validation of the last value if all Validation instances are valid
229+
* or an invalid Validation containing an accumulated List of errors.
230+
* @throws NullPointerException if values is null
231+
*/
232+
public static <E, T> Validation<Seq<E>, T> all(Traversable<? extends Validation <? extends E, ? extends T>> values) {
233+
Objects.requireNonNull(values, "values is null");
234+
Iterable<Validation<List<? extends E>, ? extends T>> mapped = values.map(v -> v.mapError(List::of));
235+
return sequence(mapped).map(Traversable::last);
236+
}
237+
190238
/**
191239
* Transforms this {@code Validation}.
192240
*

src/test/java/io/vavr/control/ValidationTest.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
import io.vavr.AbstractValueTest;
2222
import io.vavr.collection.CharSeq;
23-
import io.vavr.collection.Seq;
2423
import io.vavr.collection.List;
24+
import io.vavr.collection.Seq;
25+
import io.vavr.collection.Traversable;
2526
import org.junit.Test;
2627

2728
import java.util.ArrayList;
@@ -153,6 +154,58 @@ public void shouldCreateInvalidWhenSequencingAnInvalid() {
153154
assertThat(actual).isEqualTo(Validation.invalid(List.of("error1", "error2", "error3", "error4")));
154155
}
155156

157+
// -- Validation.all
158+
159+
@Test(expected = NullPointerException.class)
160+
public void shouldThrowWhenAllCombiningNull() {
161+
Validation.all((Traversable<? extends Validation<?, ?>>) null);
162+
}
163+
164+
@Test
165+
public void shouldCreateValidWhenAllCombiningValids() {
166+
final Validation<Seq<String>, Integer> actual = Validation.all(List.of(
167+
Validation.valid(1),
168+
Validation.valid(2)
169+
));
170+
assertThat(actual).isEqualTo(Validation.valid(List.of(1, 2).last()));
171+
}
172+
173+
@Test
174+
public void shouldCreateInvalidWhenAllCombiningInvalid() {
175+
final Validation<Seq<String>, Integer> actual = Validation.all(List.of(
176+
Validation.valid(1),
177+
Validation.invalid("error1"),
178+
Validation.valid(2),
179+
Validation.invalid("error2")
180+
));
181+
assertThat(actual).isEqualTo(Validation.invalid(List.of("error1", "error2")));
182+
}
183+
184+
@Test(expected = NullPointerException.class)
185+
public void shouldThrowWhenAllCombiningVargNull() {
186+
Validation.all((Validation<?, ?>) null);
187+
}
188+
189+
@Test
190+
public void shouldCreateValidWhenAllCombiningVargValids() {
191+
final Validation<Seq<String>, Integer> actual = Validation.all(
192+
Validation.valid(1),
193+
Validation.valid(2)
194+
);
195+
assertThat(actual).isEqualTo(Validation.valid(List.of(1, 2).last()));
196+
}
197+
198+
@Test
199+
public void shouldCreateInvalidWhenAllCombiningVargInvalid() {
200+
Validation<Seq<String>, Integer> actual = Validation.all(
201+
Validation.valid(1),
202+
Validation.invalid("error2"),
203+
Validation.valid(3),
204+
Validation.invalid("error4")
205+
);
206+
assertThat(actual).isEqualTo(Validation.invalid(List.of("error2", "error4")));
207+
}
208+
156209
// -- Validation.traverse
157210

158211
@Test(expected = NullPointerException.class)

0 commit comments

Comments
 (0)