Skip to content

Commit

Permalink
Updates StructElement.update() to be more Java-friendly (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt authored Dec 13, 2023
1 parent fbd44b4 commit 993cad6
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
3 changes: 2 additions & 1 deletion api/IonElement.api
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,8 @@ public abstract interface class com/amazon/ionelement/api/StructElement : com/am
public abstract fun getFields ()Ljava/util/Collection;
public abstract fun getOptional (Ljava/lang/String;)Lcom/amazon/ionelement/api/AnyElement;
public abstract fun mutableFields ()Lcom/amazon/ionelement/api/MutableStructFields;
public abstract fun update (Lkotlin/jvm/functions/Function1;)Lcom/amazon/ionelement/api/StructElement;
public abstract fun update (Ljava/util/function/Consumer;)Lcom/amazon/ionelement/api/StructElement;
public abstract synthetic fun update (Lkotlin/jvm/functions/Function1;)Lcom/amazon/ionelement/api/StructElement;
public abstract fun withAnnotations (Ljava/lang/Iterable;)Lcom/amazon/ionelement/api/StructElement;
public abstract fun withAnnotations ([Ljava/lang/String;)Lcom/amazon/ionelement/api/StructElement;
public abstract fun withMeta (Ljava/lang/String;Ljava/lang/Object;)Lcom/amazon/ionelement/api/StructElement;
Expand Down
38 changes: 35 additions & 3 deletions src/com/amazon/ionelement/api/IonElement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.amazon.ion.Decimal
import com.amazon.ion.IonWriter
import com.amazon.ion.Timestamp
import java.math.BigInteger
import java.util.function.Consumer

/**
* Represents an immutable Ion element.
Expand Down Expand Up @@ -446,12 +447,43 @@ public interface StructElement : ContainerElement {
/** A mutable shallow copy of this struct's fields */
public fun mutableFields(): MutableStructFields

/** Creates a new struct from this struct after executing the lambda body with [MutableStructFields] as the
* receiver.
/*
* Note about the update() functions
*
* fun update(body: MutableStructFields.() -> Unit)
*
* This gives us an idiomatic Kotlin experience, but requires Java callers to add `return Unit.INSTANCE` to any
* lambda functions. Therefore, we also have:
*
* fun update(mutator: Consumer<MutableStructFields>)
*
* This gives us an idiomatic Java experience because Java callers can now pass in a lambda function with a
* void return. However, it clashes with the Kotlin-specific function if you try to pass in a lambda without the
* enclosing curly braces. E.g.:
*
* myStruct.update(s -> s.clearField("foo"));
*
* Ambiguous method call. Both update (Function1<? super MutableStructFields, Unit>) in StructElement and
* update(Consumer<MutableStructFields>) in StructElement match
*
* In order to solve this, the Kotlin-specific version of the function is marked with @JvmSynthetic to make it
* inaccessible from Java at compile time.
*/

/**
* Creates a new copy of this struct with the updates in [mutator] applied to the fields of the new struct.
*
* Annotations and metas are preserved.
*/
@JvmSynthetic
public fun update(mutator: MutableStructFields.() -> Unit): StructElement

/**
* Creates a new copy of this struct with the updates in [mutator] applied to the fields of the new struct.
*
* Annotations and metas are preserved.
*/
public fun update(body: MutableStructFields.() -> Unit): StructElement
public fun update(mutator: Consumer<MutableStructFields>): StructElement

override fun copy(annotations: List<String>, metas: MetaContainer): StructElement
override fun withAnnotations(vararg additionalAnnotations: String): StructElement
Expand Down
11 changes: 9 additions & 2 deletions src/com/amazon/ionelement/impl/StructElementImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.amazon.ion.IonWriter
import com.amazon.ionelement.api.*
import com.amazon.ionelement.api.PersistentMetaContainer
import com.amazon.ionelement.api.constraintError
import java.util.function.Consumer
import kotlinx.collections.immutable.PersistentCollection
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.PersistentMap
Expand Down Expand Up @@ -75,9 +76,15 @@ internal class StructElementImpl(
)
}

override fun update(body: MutableStructFields.() -> Unit): StructElement {
override fun update(mutator: MutableStructFields.() -> Unit): StructElement {
val mutableFields = mutableFields()
body(mutableFields)
mutableFields.apply(mutator)
return ionStructOf(mutableFields, annotations, metas)
}

override fun update(mutator: Consumer<MutableStructFields>): StructElement {
val mutableFields = mutableFields()
mutator.accept(mutableFields)
return ionStructOf(mutableFields, annotations, metas)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ void createUpdatedStructFromExistingStruct() {
StructElement expected = loadSingleElement(
"{name:\"Alice\",scores:{darts:100,billiards:30,pingPong:200,}}").asStruct();

StructElement updated = original.update(fields -> {
StructElement updated = original.update(fields ->
fields.set("scores",
original.get("scores").asStruct().update(scoreFields -> {
fields.get("scores").asStruct().update(scoreFields -> {
scoreFields.add("pingPong", Ion.ionInt(200));
scoreFields.set("billiards", Ion.ionInt(30));
return Unit.INSTANCE;
}));
return Unit.INSTANCE;
});
})
));

assertEquals(expected, updated);
}
Expand Down

0 comments on commit 993cad6

Please sign in to comment.