Skip to content

Commit

Permalink
refactor!: improve reducer API
Browse files Browse the repository at this point in the history
  • Loading branch information
tynn committed Dec 31, 2023
1 parent 821ca1e commit 12f937c
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 510 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Since the `AString` represent any generic `CharSequence`, it might be useful
to transform the value before its use.

aString.format("arg", AppId)
aString.defaultIfNull("value")
aString.ifNull("value")
aString.nullIfBlank()
aString.mapToString()
aString.trim()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static xyz.tynn.astring.AStringFactory.createFromCharSequence;
import static xyz.tynn.astring.AStringFactory.getAppId;
import static xyz.tynn.astring.AStringFactory.getAppVersion;
import static xyz.tynn.astring.test.AStringAssert.assertParcelableAStringInvocation;

import androidx.annotation.NonNull;
Expand All @@ -17,7 +19,7 @@

public class AStringReducerAndroidTest {

private final Iterable<AString> aStrings = List.of(AString.Null);
private final Iterable<AString> aStrings = List.of(getAppId(), getAppVersion());

@Test
public void delegate_should_implement_parcelable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@

public class ParcelableAStringTest {

private static final Locale LOCALE = Locale.UK;
private static final int RES_ID = 123;
private static final int QUANTITY = 456;
private static final AString FORMAT = new FormatAString();
private static final Object[] FORMAT_ARGS = {"arg1", 2, 3L, 4.5, 6F, new Date(), Wrapper.wrap("test-string")};

Expand All @@ -36,24 +33,24 @@ public void Wrapper_should_implement_parcelable() {

@Test
public void Format_should_implement_parcelable() {
assertParcelableAStringEquality(Format.wrap(FORMAT, LOCALE, FORMAT_ARGS));
assertParcelableAStringInvocation(Format.wrap(FORMAT, LOCALE, FORMAT_ARGS));
assertParcelableAStringEquality(Format.wrap(FORMAT, Locale.UK, FORMAT_ARGS));
assertParcelableAStringInvocation(Format.wrap(FORMAT, Locale.UK, FORMAT_ARGS));
assertParcelableAStringEquality(Format.wrap(FORMAT, null, FORMAT_ARGS));
assertParcelableAStringInvocation(Format.wrap(FORMAT, null, FORMAT_ARGS));
assertParcelableAStringEquality(Format.wrap(FORMAT, LOCALE, null));
assertParcelableAStringInvocation(Format.wrap(FORMAT, LOCALE, null));
assertParcelableAStringEquality(Format.wrap(FORMAT, Locale.UK, null));
assertParcelableAStringInvocation(Format.wrap(FORMAT, Locale.UK, null));
assertParcelableAStringEquality(Format.wrap(FORMAT, null, null));
assertParcelableAStringInvocation(Format.wrap(FORMAT, null, null));
}

@Test
public void Resource_should_implement_parcelable() {
assertParcelableAStringEquality(Resource.wrap(RES_ID, null));
assertParcelableAStringEquality(Resource.wrap(RES_ID, QUANTITY));
assertParcelableAStringEquality(Resource.wrap(RES_ID, null, FORMAT_ARGS));
assertParcelableAStringEquality(Resource.wrap(RES_ID, QUANTITY, FORMAT_ARGS));
assertParcelableAStringEquality(Resource.wrap(RES_ID, null, null));
assertParcelableAStringEquality(Resource.wrap(RES_ID, QUANTITY, null));
assertParcelableAStringEquality(Resource.wrap(123, null));
assertParcelableAStringEquality(Resource.wrap(123, 456));
assertParcelableAStringEquality(Resource.wrap(123, null, FORMAT_ARGS));
assertParcelableAStringEquality(Resource.wrap(123, 456, FORMAT_ARGS));
assertParcelableAStringEquality(Resource.wrap(123, null, null));
assertParcelableAStringEquality(Resource.wrap(123, 456, null));
}

@Test
Expand All @@ -65,18 +62,22 @@ public void Delegate_should_implement_parcelable() {
assertParcelableAStringEquality(Delegate.wrap(Provider.AppVersion));
assertParcelableAStringInvocation(Delegate.wrap(Provider.AppVersion));
assertParcelableAStringInvocation(Delegate.wrap(Object::toString));
assertParcelableAStringIdentity(Delegate.wrap((AString) null, null));
assertParcelableAStringInvocation(Delegate.wrap((AString) null, null));
assertParcelableAStringEquality(Delegate.wrap(FORMAT, Predicate.NonBlank));
assertParcelableAStringInvocation(Delegate.wrap(FORMAT, Predicate.NonBlank));
assertParcelableAStringEquality(Delegate.wrap(FORMAT, Predicate.NonEmpty));
assertParcelableAStringInvocation(Delegate.wrap(FORMAT, Predicate.NonEmpty));
assertParcelableAStringEquality(Delegate.wrap(FORMAT, Predicate.NonNull));
assertParcelableAStringInvocation(Delegate.wrap(FORMAT, Predicate.NonNull));
assertParcelableAStringEquality(Delegate.wrap(FORMAT, Transformer.ToString));
assertParcelableAStringInvocation(Delegate.wrap(FORMAT, Transformer.ToString));
assertParcelableAStringEquality(Delegate.wrap(FORMAT, Transformer.Trim));
assertParcelableAStringInvocation(Delegate.wrap(FORMAT, Transformer.Trim));
assertParcelableAStringIdentity(Delegate.wrap((AString.Transformer) null, null));
assertParcelableAStringInvocation(Delegate.wrap((AString.Transformer) null, null));
assertParcelableAStringEquality(Delegate.wrap(Transformer.ToString, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Transformer.ToString, FORMAT));
assertParcelableAStringEquality(Delegate.wrap(Transformer.Trim, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Transformer.Trim, FORMAT));
assertParcelableAStringIdentity(Delegate.wrap((AString.Reducer) null));
assertParcelableAStringInvocation(Delegate.wrap((AString.Reducer) null));
assertParcelableAStringEquality(Delegate.wrap(Predicate.AnyValue, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Predicate.AnyValue, FORMAT));
assertParcelableAStringEquality(Delegate.wrap(Predicate.NonBlank, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Predicate.NonBlank, FORMAT));
assertParcelableAStringEquality(Delegate.wrap(Predicate.NonEmpty, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Predicate.NonEmpty, FORMAT));
assertParcelableAStringEquality(Delegate.wrap(Predicate.NonNull, FORMAT));
assertParcelableAStringInvocation(Delegate.wrap(Predicate.NonNull, FORMAT));
}

private static class FormatAString implements AString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ import xyz.tynn.astring.test.AStringAssert.assertParcelableAStringInvocation
@InefficientAStringApi
internal class AStringReducerKtAndroidTest {

private val aString1 = AString.Null
private val aString2 = "+".asAString()
private val aStrings = listOf(aString1, aString2)
private val aStringIterable = sequenceOf(aString1, aString2).asIterable()
private val aStrings = listOf(AString.Null, AppId)
private val aStringIterable = sequenceOf(AString.Null, AppId).asIterable()

@Test
fun firstNonBlank_should_be_parcelable() {
assertParcelableAStringEquality(firstNonBlank(aString1, aString2))
assertParcelableAStringInvocation(firstNonBlank(aString1, aString2))
assertParcelableAStringEquality(firstNonBlank(AString.Null, AppId))
assertParcelableAStringInvocation(firstNonBlank(AString.Null, AppId))
assertParcelableAStringEquality(aStrings.firstNonBlank())
assertParcelableAStringInvocation(aStrings.firstNonBlank())
assertParcelableAStringEquality(aStringIterable.firstNonBlank())
Expand All @@ -29,8 +27,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun firstNonEmpty_should_be_parcelable() {
assertParcelableAStringEquality(firstNonEmpty(aString1, aString2))
assertParcelableAStringInvocation(firstNonEmpty(aString1, aString2))
assertParcelableAStringEquality(firstNonEmpty(AString.Null, AppId))
assertParcelableAStringInvocation(firstNonEmpty(AString.Null, AppId))
assertParcelableAStringEquality(aStrings.firstNonEmpty())
assertParcelableAStringInvocation(aStrings.firstNonEmpty())
assertParcelableAStringEquality(aStringIterable.firstNonEmpty())
Expand All @@ -39,8 +37,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun firstNonNull_should_be_parcelable() {
assertParcelableAStringEquality(firstNonNull(aString1, aString2))
assertParcelableAStringInvocation(firstNonNull(aString1, aString2))
assertParcelableAStringEquality(firstNonNull(AString.Null, AppId))
assertParcelableAStringInvocation(firstNonNull(AString.Null, AppId))
assertParcelableAStringEquality(aStrings.firstNonNull())
assertParcelableAStringInvocation(aStrings.firstNonNull())
assertParcelableAStringEquality(aStringIterable.firstNonNull())
Expand All @@ -49,8 +47,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun join_should_be_parcelable() {
assertParcelableAStringEquality(join(aString1, aString2, separator = "-"))
assertParcelableAStringInvocation(join(aString1, aString2, separator = "-"))
assertParcelableAStringEquality(join(AString.Null, AppId, separator = "-"))
assertParcelableAStringInvocation(join(AString.Null, AppId, separator = "-"))
assertParcelableAStringEquality(aStrings.join(separator = "-"))
assertParcelableAStringInvocation(aStrings.join(separator = "-"))
assertParcelableAStringEquality(aStringIterable.join(separator = "-"))
Expand All @@ -59,8 +57,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun joinNonBlank_should_be_parcelable() {
assertParcelableAStringEquality(joinNonBlank(aString1, aString2, separator = "-"))
assertParcelableAStringInvocation(joinNonBlank(aString1, aString2, separator = "-"))
assertParcelableAStringEquality(joinNonBlank(AString.Null, AppId, separator = "-"))
assertParcelableAStringInvocation(joinNonBlank(AString.Null, AppId, separator = "-"))
assertParcelableAStringEquality(aStrings.joinNonBlank(separator = "-"))
assertParcelableAStringInvocation(aStrings.joinNonBlank(separator = "-"))
assertParcelableAStringEquality(aStringIterable.joinNonBlank(separator = "-"))
Expand All @@ -69,8 +67,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun joinNonEmpty_should_be_parcelable() {
assertParcelableAStringEquality(joinNonEmpty(aString1, aString2, separator = "-"))
assertParcelableAStringInvocation(joinNonEmpty(aString1, aString2, separator = "-"))
assertParcelableAStringEquality(joinNonEmpty(AString.Null, AppId, separator = "-"))
assertParcelableAStringInvocation(joinNonEmpty(AString.Null, AppId, separator = "-"))
assertParcelableAStringEquality(aStrings.joinNonEmpty(separator = "-"))
assertParcelableAStringInvocation(aStrings.joinNonEmpty(separator = "-"))
assertParcelableAStringEquality(aStringIterable.joinNonEmpty(separator = "-"))
Expand All @@ -79,8 +77,8 @@ internal class AStringReducerKtAndroidTest {

@Test
fun joinNonNull_should_be_parcelable() {
assertParcelableAStringEquality(joinNonNull(aString1, aString2, separator = "-"))
assertParcelableAStringInvocation(joinNonNull(aString1, aString2, separator = "-"))
assertParcelableAStringEquality(joinNonNull(AString.Null, AppId, separator = "-"))
assertParcelableAStringInvocation(joinNonNull(AString.Null, AppId, separator = "-"))
assertParcelableAStringEquality(aStrings.joinNonNull(separator = "-"))
assertParcelableAStringInvocation(aStrings.joinNonNull(separator = "-"))
assertParcelableAStringEquality(aStringIterable.joinNonNull(separator = "-"))
Expand All @@ -91,15 +89,15 @@ internal class AStringReducerKtAndroidTest {
@Suppress("RedundantSamConstructor")
fun interface_should_not_be_efficient() {
assertNotEquals(
reduce(aString1, aString2, reducer = AString.Reducer { "" }),
reduce(aString1, aString2, reducer = AString.Reducer { "" }),
reduce(AString.Null, AppId, reducer = AString.Reducer { "" }),
reduce(AString.Null, AppId, reducer = AString.Reducer { "" }),
)
assertNotEquals(
reduce(aString1, aString2, reducer = AString.Reducer { "" }),
reduce(AString.Null, AppId, reducer = AString.Reducer { "" }),
aStrings.reduce(reducer = AString.Reducer { "" }),
)
assertNotEquals(
reduce(aString1, aString2, reducer = AString.Reducer { "" }),
reduce(AString.Null, AppId, reducer = AString.Reducer { "" }),
aStringIterable.reduce(reducer = AString.Reducer { "" }),
)
}
Expand All @@ -108,31 +106,31 @@ internal class AStringReducerKtAndroidTest {
fun interface_val_should_be_efficient() {
val function = AString.Reducer { "" }
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
reduce(AString.Null, AppId, reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStrings.reduce(reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStringIterable.reduce(reducer = function),
)
}

@Test
fun instance_should_be_efficient() {
assertEquals(
reduce(aString1, aString2, reducer = Reducer()),
reduce(aString1, aString2, reducer = Reducer()),
reduce(AString.Null, AppId, reducer = Reducer()),
reduce(AString.Null, AppId, reducer = Reducer()),
)
assertEquals(
reduce(aString1, aString2, reducer = Reducer()),
reduce(AString.Null, AppId, reducer = Reducer()),
aStrings.reduce(reducer = Reducer()),
)
assertEquals(
reduce(aString1, aString2, reducer = Reducer()),
reduce(AString.Null, AppId, reducer = Reducer()),
aStringIterable.reduce(reducer = Reducer()),
)
}
Expand All @@ -141,31 +139,31 @@ internal class AStringReducerKtAndroidTest {
fun instance_val_should_be_efficient() {
val function = Reducer()
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
reduce(AString.Null, AppId, reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStrings.reduce(reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStringIterable.reduce(reducer = function),
)
}

@Test
fun function_reference_should_be_efficient() {
assertEquals(
reduce(aString1, aString2, reducer = ::function),
reduce(aString1, aString2, reducer = ::function),
reduce(AString.Null, AppId, reducer = ::function),
reduce(AString.Null, AppId, reducer = ::function),
)
assertEquals(
reduce(aString1, aString2, reducer = ::function),
reduce(AString.Null, AppId, reducer = ::function),
aStrings.reduce(reducer = ::function),
)
assertEquals(
reduce(aString1, aString2, reducer = ::function),
reduce(AString.Null, AppId, reducer = ::function),
aStringIterable.reduce(reducer = ::function),
)
}
Expand All @@ -174,31 +172,31 @@ internal class AStringReducerKtAndroidTest {
fun function_reference_val_should_be_efficient() {
val function = ::function
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
reduce(AString.Null, AppId, reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStrings.reduce(reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStringIterable.reduce(reducer = function),
)
}

@Test
fun lambda_should_not_be_efficient() {
assertNotEquals(
reduce(aString1, aString2) { it.toString() },
reduce(aString1, aString2) { it.toString() },
reduce(AString.Null, AppId) { it.toString() },
reduce(AString.Null, AppId) { it.toString() },
)
assertNotEquals(
reduce(aString1, aString2) { it.toString() },
reduce(AString.Null, AppId) { it.toString() },
aStrings.reduce { it.toString() },
)
assertNotEquals(
reduce(aString1, aString2) { it.toString() },
reduce(AString.Null, AppId) { it.toString() },
aStringIterable.reduce { it.toString() },
)
}
Expand All @@ -207,15 +205,15 @@ internal class AStringReducerKtAndroidTest {
fun lambda_val_should_be_efficient() {
val function = { _: Iterable<CharSequence?> -> "" }
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
reduce(AString.Null, AppId, reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStrings.reduce(reducer = function),
)
assertEquals(
reduce(aString1, aString2, reducer = function),
reduce(AString.Null, AppId, reducer = function),
aStringIterable.reduce(reducer = function),
)
}
Expand Down
11 changes: 6 additions & 5 deletions astring/src/main/java/xyz/tynn/astring/Delegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
import androidx.annotation.Nullable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Objects;

final class Delegate implements AString {
Expand All @@ -43,21 +43,22 @@ static AString wrap(AString.Provider provider) {

@InefficientAStringApi
static AString wrap(AString.Reducer reducer, AString... aStrings) {
if (reducer == null || aStrings == null || aStrings.length == 0) return Null;
return new Delegate(new Serializer.Reducer(reducer), aStrings);
if (reducer == null || aStrings == null) return Null;
AString aString = Wrapper.maybeReduce(reducer, aStrings);
return aString != null ? aString : new Delegate(new Serializer.Reducer(reducer), aStrings);
}

@InefficientAStringApi
static AString wrap(AString.Reducer reducer, Iterable<AString> aStrings) {
if (reducer == null || aStrings == null) return Null;
Collection<AString> list = aStrings instanceof Collection<?>
? (Collection<AString>) aStrings : new ArrayList<>();
? (Collection<AString>) aStrings : new LinkedList<>();
if (list != aStrings) for (AString aString : aStrings) list.add(aString);
return wrap(reducer, list.toArray(EMPTY));
}

@InefficientAStringApi
static AString wrap(AString aString, Transformer transformer) {
static AString wrap(Transformer transformer, AString aString) {
if (transformer == null) return Null;
if (aString == null || aString == Null) return Wrapper.wrap(transformer.invoke(null));
if (aString instanceof Wrapper) return ((Wrapper) aString).map(transformer);
Expand Down
Loading

0 comments on commit 12f937c

Please sign in to comment.