diff --git a/dfu/src/main/java/org/enginehub/linbus/dfu/LinOps.java b/dfu/src/main/java/org/enginehub/linbus/dfu/LinOps.java index 6513e66..1dc6052 100644 --- a/dfu/src/main/java/org/enginehub/linbus/dfu/LinOps.java +++ b/dfu/src/main/java/org/enginehub/linbus/dfu/LinOps.java @@ -462,10 +462,26 @@ private static void addHomogenized( return; } for (LinTag element : elements) { - builder.add(element instanceof LinCompoundTag existing ? existing : wrapElement(element)); + builder.add(wrapElementIfNeeded(element)); } } + private static LinCompoundTag wrapElementIfNeeded(LinTag element) { + // If the element is a compound that isn't already wrapper-looking, we can use it as-is. + // Otherwise, we need to wrap it again to preserve the value when unwrapped later. + if (element instanceof LinCompoundTag compound && unwrapOrNull(compound) == null) { + return compound; + } + return LinCompoundTag.of(Map.of("", element)); + } + + private static @Nullable LinTag unwrapOrNull(LinCompoundTag compound) { + if (compound.value().size() == 1) { + return compound.value().get(""); + } + return null; + } + /** * {@return the shared type of {@code elements}, or the compound type if they are heterogeneous} * @@ -510,13 +526,9 @@ private static List> unwrapStoredList(LinListTag list) { return result; } - private static LinCompoundTag wrapElement(LinTag element) { - return LinCompoundTag.of(Map.of("", element)); - } - private static LinTag tryUnwrap(LinTag element) { - if (element instanceof LinCompoundTag compound && compound.value().size() == 1) { - LinTag unwrapped = compound.value().get(""); + if (element instanceof LinCompoundTag compound) { + LinTag unwrapped = unwrapOrNull(compound); if (unwrapped != null) { return unwrapped; } diff --git a/dfu/src/test/java/org/enginehub/linbus/dfu/LinOpsListTest.java b/dfu/src/test/java/org/enginehub/linbus/dfu/LinOpsListTest.java index b5b0345..ea12504 100644 --- a/dfu/src/test/java/org/enginehub/linbus/dfu/LinOpsListTest.java +++ b/dfu/src/test/java/org/enginehub/linbus/dfu/LinOpsListTest.java @@ -116,6 +116,39 @@ void createListWrapsOnlyNonCompoundElementsWhenMixedWithCompound() { assertThat(OPS.getStream(list)).hasStreamResultThat().containsExactly(a, LinIntTag.of(2)).inOrder(); } + @Test + @NbtOpsBehavior + void createListWrapsWrapperShapedCompoundElements() { + LinCompoundTag wrapperShaped = wrap(LinIntTag.of(1)); + LinCompoundTag other = LinCompoundTag.builder().putInt("a", 1).build(); + LinTag list = OPS.createList(Stream.of(wrapperShaped, other)); + assertThat(list) + .isEqualTo(LinListTag.of(LinTagType.compoundTag(), List.of(wrap(wrapperShaped), other))); + assertThat(OPS.getStream(list)) + .hasStreamResultThat().containsExactly(wrapperShaped, other).inOrder(); + } + + @Test + @NbtOpsBehavior + void createListWrapsWrapperShapedCompoundElementsAlone() { + LinCompoundTag wrapperShaped = wrap(LinIntTag.of(1)); + LinTag list = OPS.createList(Stream.of(wrapperShaped)); + assertThat(list) + .isEqualTo(LinListTag.of(LinTagType.compoundTag(), List.of(wrap(wrapperShaped)))); + assertThat(OPS.getStream(list)) + .hasStreamResultThat().containsExactly(wrapperShaped).inOrder(); + } + + @Test + @NbtOpsBehavior + void createListOfOnlyWrapperShapedCompoundsRoundTrips() { + LinCompoundTag a = wrap(LinIntTag.of(1)); + LinCompoundTag b = wrap(LinStringTag.of("x")); + LinTag list = OPS.createList(Stream.of(a, b)); + assertThat(list).isEqualTo(LinListTag.of(LinTagType.compoundTag(), List.of(wrap(a), wrap(b)))); + assertThat(OPS.getStream(list)).hasStreamResultThat().containsExactly(a, b).inOrder(); + } + @Test void mergeToListOntoEndTakesElementType() { assertThat(OPS.mergeToList(LinEndTag.instance(), LinIntTag.of(1))) @@ -157,6 +190,20 @@ void mergeToListGrowsWrappedList() { ))); } + @Test + @NbtOpsBehavior + void mergeToListWrapsWrapperShapedCompound() { + LinCompoundTag wrapperShaped = wrap(LinIntTag.of(2)); + LinTag list = OPS.mergeToList( + LinListTag.of(LinTagType.intTag(), List.of(LinIntTag.of(1))), wrapperShaped + ).result().orElseThrow(); + assertThat(list).isEqualTo(LinListTag.of(LinTagType.compoundTag(), List.of( + wrap(LinIntTag.of(1)), wrap(wrapperShaped) + ))); + assertThat(OPS.getStream(list)) + .hasStreamResultThat().containsExactly(LinIntTag.of(1), wrapperShaped).inOrder(); + } + @Test void mergeToListAppendsSeveralValues() { assertThat(OPS.mergeToList(