Skip to content

Commit 4eada0d

Browse files
committed
Fix comment ops and recover annotation values in reflective structures
1 parent 4571b65 commit 4eada0d

File tree

19 files changed

+713
-211
lines changed

19 files changed

+713
-211
lines changed

src/main/java/dev/lukebemish/codecextras/comments/CommentMapCodec.java

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -47,67 +47,76 @@ public <T> DataResult<A> decode(DynamicOps<T> ops, MapLike<T> input) {
4747

4848
@Override
4949
public <T> RecordBuilder<T> encode(A input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
50-
final RecordBuilder<T> builder = delegate.encode(input, ops, prefix);
51-
52-
return new RecordBuilder<>() {
53-
RecordBuilder<T> mutableBuilder = builder;
54-
55-
@Override
56-
public DynamicOps<T> ops() {
57-
return builder.ops();
50+
prefix = delegate.encode(input, ops, prefix);
51+
if (prefix instanceof CommentRecordBuilder<T> commentRecordBuilder) {
52+
for (Map.Entry<String, String> entry : comments.entrySet()) {
53+
prefix = commentRecordBuilder.comment(ops.createString(entry.getKey()), ops.createString(entry.getValue()));
5854
}
59-
60-
@Override
61-
public RecordBuilder<T> add(T key, T value) {
62-
mutableBuilder = mutableBuilder.add(key, value);
63-
return this;
64-
}
65-
66-
@Override
67-
public RecordBuilder<T> add(T key, DataResult<T> value) {
68-
mutableBuilder = mutableBuilder.add(key, value);
69-
return this;
70-
}
71-
72-
@Override
73-
public RecordBuilder<T> add(DataResult<T> key, DataResult<T> value) {
74-
mutableBuilder = mutableBuilder.add(key, value);
75-
return this;
76-
}
77-
78-
@Override
79-
public RecordBuilder<T> withErrorsFrom(DataResult<?> result) {
80-
mutableBuilder = mutableBuilder.withErrorsFrom(result);
81-
return this;
82-
}
83-
84-
@Override
85-
public RecordBuilder<T> setLifecycle(Lifecycle lifecycle) {
86-
mutableBuilder = mutableBuilder.setLifecycle(lifecycle);
87-
return this;
88-
}
89-
90-
@Override
91-
public RecordBuilder<T> mapError(UnaryOperator<String> onError) {
92-
mutableBuilder = mutableBuilder.mapError(onError);
93-
return this;
94-
}
95-
96-
@Override
97-
public DataResult<T> build(T prefix) {
98-
DataResult<T> built = builder.build(prefix);
99-
return AccompaniedOps.find(this.ops()).map(accompaniedOps -> {
100-
Optional<CommentOps<T>> commentOps = accompaniedOps.getCompanion(CommentOps.TOKEN);
101-
if (commentOps.isPresent()) {
102-
return built.flatMap(t ->
103-
commentOps.get().commentToMap(t, comments.entrySet().stream().collect(Collectors.toMap(e ->
104-
ops.createString(e.getKey()), e -> ops.createString(e.getValue()))))
105-
);
106-
}
107-
return built;
108-
}).orElse(built);
109-
}
110-
};
55+
return prefix;
56+
} else {
57+
// Best-attempt -- anything that fails to pass the builder downstream will cause this to fail
58+
var builder = prefix;
59+
return new RecordBuilder<>() {
60+
RecordBuilder<T> mutableBuilder = builder;
61+
62+
@Override
63+
public DynamicOps<T> ops() {
64+
return builder.ops();
65+
}
66+
67+
@Override
68+
public RecordBuilder<T> add(T key, T value) {
69+
mutableBuilder = mutableBuilder.add(key, value);
70+
return this;
71+
}
72+
73+
@Override
74+
public RecordBuilder<T> add(T key, DataResult<T> value) {
75+
mutableBuilder = mutableBuilder.add(key, value);
76+
return this;
77+
}
78+
79+
@Override
80+
public RecordBuilder<T> add(DataResult<T> key, DataResult<T> value) {
81+
mutableBuilder = mutableBuilder.add(key, value);
82+
return this;
83+
}
84+
85+
@Override
86+
public RecordBuilder<T> withErrorsFrom(DataResult<?> result) {
87+
mutableBuilder = mutableBuilder.withErrorsFrom(result);
88+
return this;
89+
}
90+
91+
@Override
92+
public RecordBuilder<T> setLifecycle(Lifecycle lifecycle) {
93+
mutableBuilder = mutableBuilder.setLifecycle(lifecycle);
94+
return this;
95+
}
96+
97+
@Override
98+
public RecordBuilder<T> mapError(UnaryOperator<String> onError) {
99+
mutableBuilder = mutableBuilder.mapError(onError);
100+
return this;
101+
}
102+
103+
@Override
104+
public DataResult<T> build(T prefix) {
105+
// TODO: the RecordBuilder is now tossed out; fix this
106+
DataResult<T> built = builder.build(prefix);
107+
return AccompaniedOps.find(this.ops()).map(accompaniedOps -> {
108+
Optional<CommentOps<T>> commentOps = accompaniedOps.getCompanion(CommentOps.TOKEN);
109+
if (commentOps.isPresent()) {
110+
return built.flatMap(t ->
111+
commentOps.get().commentToMap(t, comments.entrySet().stream().collect(Collectors.toMap(e ->
112+
ops.createString(e.getKey()), e -> ops.createString(e.getValue()))))
113+
);
114+
}
115+
return built;
116+
}).orElse(built);
117+
}
118+
};
119+
}
111120
}
112121

113122
@Override
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dev.lukebemish.codecextras.comments;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import com.mojang.serialization.DataResult;
5+
import com.mojang.serialization.DynamicOps;
6+
import com.mojang.serialization.RecordBuilder;
7+
import dev.lukebemish.codecextras.companion.AccompaniedOps;
8+
import java.util.Map;
9+
import java.util.Optional;
10+
import java.util.stream.Collectors;
11+
12+
public interface CommentRecordBuilder<T> extends RecordBuilder<T> {
13+
CommentRecordBuilder<T> comment(T key, T value);
14+
15+
final class MapBuilder<T> extends AbstractUniversalBuilder<T, ImmutableMap.Builder<T, T>> implements CommentRecordBuilder<T> {
16+
private final ImmutableMap.Builder<T, T> commentsBuilder = ImmutableMap.builder();
17+
18+
public MapBuilder(final DynamicOps<T> ops) {
19+
super(ops);
20+
}
21+
22+
@Override
23+
public CommentRecordBuilder<T> comment(T key, T value) {
24+
commentsBuilder.put(key, value);
25+
return this;
26+
}
27+
28+
@Override
29+
protected ImmutableMap.Builder<T, T> initBuilder() {
30+
return ImmutableMap.builder();
31+
}
32+
33+
@Override
34+
protected ImmutableMap.Builder<T, T> append(final T key, final T value, final ImmutableMap.Builder<T, T> builder) {
35+
return builder.put(key, value);
36+
}
37+
38+
@Override
39+
protected DataResult<T> build(final ImmutableMap.Builder<T, T> builder, final T prefix) {
40+
var built = ops().mergeToMap(prefix, builder.buildKeepingLast());
41+
var comments = commentsBuilder.build();
42+
return AccompaniedOps.find(this.ops()).map(accompaniedOps -> {
43+
Optional<CommentOps<T>> commentOps = accompaniedOps.getCompanion(CommentOps.TOKEN);
44+
if (commentOps.isPresent()) {
45+
return built.flatMap(t ->
46+
commentOps.get().commentToMap(t, comments.entrySet().stream().collect(
47+
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
48+
))
49+
);
50+
}
51+
return built;
52+
}).orElse(built);
53+
}
54+
}
55+
}

src/main/java/dev/lukebemish/codecextras/companion/AccompaniedOps.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ static <T> Optional<AccompaniedOps<T>> find(DynamicOps<T> ops) {
1515
return companion;
1616
}
1717
}
18+
if (ops instanceof AccompaniedOps<T> accompaniedOps) {
19+
return Optional.of(accompaniedOps);
20+
}
1821
return Optional.empty();
1922
}
2023
}

src/main/java/dev/lukebemish/codecextras/compat/jankson/JanksonOps.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package dev.lukebemish.codecextras.compat.jankson;
22

3-
import blue.endless.jankson.*;
3+
import blue.endless.jankson.JsonArray;
4+
import blue.endless.jankson.JsonElement;
5+
import blue.endless.jankson.JsonNull;
6+
import blue.endless.jankson.JsonObject;
7+
import blue.endless.jankson.JsonPrimitive;
48
import com.google.common.collect.Lists;
59
import com.mojang.datafixers.util.Pair;
610
import com.mojang.serialization.DataResult;
711
import com.mojang.serialization.DynamicOps;
812
import com.mojang.serialization.MapLike;
13+
import com.mojang.serialization.RecordBuilder;
914
import dev.lukebemish.codecextras.comments.CommentOps;
15+
import dev.lukebemish.codecextras.comments.CommentRecordBuilder;
1016
import dev.lukebemish.codecextras.companion.AccompaniedOps;
1117
import dev.lukebemish.codecextras.companion.Companion;
1218
import java.util.List;
@@ -28,6 +34,11 @@ public <O extends Companion.CompanionToken, C extends Companion<JsonElement, O>>
2834
}
2935
return super.getCompanion(token);
3036
}
37+
38+
@Override
39+
public RecordBuilder<JsonElement> mapBuilder() {
40+
return new CommentRecordBuilder.MapBuilder<>(this);
41+
}
3142
};
3243

3344
@Override

src/main/java/dev/lukebemish/codecextras/compat/nightconfig/CommentedNightConfigOps.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.electronwill.nightconfig.core.CommentedConfig;
44
import com.electronwill.nightconfig.core.Config;
5+
import com.mojang.serialization.RecordBuilder;
6+
import dev.lukebemish.codecextras.comments.CommentRecordBuilder;
57
import dev.lukebemish.codecextras.companion.AccompaniedOps;
68

79
public abstract class CommentedNightConfigOps<T extends CommentedConfig> extends NightConfigOps<T> implements AccompaniedOps<Object> {
@@ -13,4 +15,9 @@ public T copyConfig(Config config) {
1315
}
1416
return out;
1517
}
18+
19+
@Override
20+
public RecordBuilder<Object> mapBuilder() {
21+
return new CommentRecordBuilder.MapBuilder<>(this);
22+
}
1623
}

src/main/java/dev/lukebemish/codecextras/compat/nightconfig/NightConfigOps.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,30 @@ public Object emptyMap() {
121121

122122
@Override
123123
public DataResult<Object> mergeToMap(Object map, Object key, Object value) {
124-
if (map instanceof Config config) {
125-
Config newConfig = copyConfig(config);
126-
if (key instanceof String string) {
127-
newConfig.set(string, value);
128-
return DataResult.success(newConfig);
129-
}
130-
return DataResult.error(() -> "Not a string: " + key);
124+
Config config;
125+
if (map instanceof Config isConfig) {
126+
config = isConfig;
127+
} else if (map == empty()) {
128+
config = newConfig();
129+
} else {
130+
return DataResult.error(() -> "Not a map: " + map);
131+
}
132+
Config newConfig = copyConfig(config);
133+
if (key instanceof String string) {
134+
newConfig.set(string, value);
135+
return DataResult.success(newConfig);
131136
}
132-
return DataResult.error(() -> "Not a map: " + map);
137+
return DataResult.error(() -> "Not a string: " + key);
133138
}
134139

135140
@Override
136141
public DataResult<Object> mergeToMap(Object map, MapLike<Object> values) {
137-
if (!(map instanceof Config config)) {
142+
Config config;
143+
if (map instanceof Config isConfig) {
144+
config = isConfig;
145+
} else if (map == empty()) {
146+
config = newConfig();
147+
} else {
138148
return DataResult.error(() -> "Not a map: " + map);
139149
}
140150
Config newConfig = copyConfig(config);
@@ -154,7 +164,12 @@ public DataResult<Object> mergeToMap(Object map, MapLike<Object> values) {
154164

155165
@Override
156166
public DataResult<Object> mergeToMap(Object map, Map<Object, Object> values) {
157-
if (!(map instanceof Config config)) {
167+
Config config;
168+
if (map instanceof Config isConfig) {
169+
config = isConfig;
170+
} else if (map == empty()) {
171+
config = newConfig();
172+
} else {
158173
return DataResult.error(() -> "Not a map: " + map);
159174
}
160175
Config newConfig = copyConfig(config);

src/main/java/dev/lukebemish/codecextras/structured/Annotation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public class Annotation {
3737
* @return the value of the annotation, if present
3838
* @param <A> the type of the annotation value
3939
*/
40-
public static <A> Optional<A> get(Keys<Identity.Mu, Object> keys, Key<A> key) {
40+
public static <A> Optional<A> get(
41+
Keys<Identity.Mu, Object> keys, Key<A> key) {
4142
return keys.get(key).map(app -> Identity.unbox(app).value());
4243
}
4344

src/main/java/dev/lukebemish/codecextras/structured/RecordStructure.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,33 +190,45 @@ public <T> Key<Optional<T>> addOptional(String name, Structure<T> structure, Fun
190190
return key;
191191
}
192192

193-
public Function<Container, OptionalInt> addOptionalInt(String name, Function<A, OptionalInt> getter) {
194-
return addOptional(name, Structure.INT, getter.andThen(o -> {
193+
public Function<Container, OptionalInt> addOptionalInt(String name, Structure<Integer> structure, Function<A, OptionalInt> getter) {
194+
return addOptional(name, structure, getter.andThen(o -> {
195195
if (o.isPresent()) {
196196
return Optional.of(o.getAsInt());
197197
}
198198
return Optional.empty();
199199
})).andThen(o -> o.map(OptionalInt::of).orElse(OptionalInt.empty()));
200200
}
201201

202-
public Function<Container, OptionalDouble> addOptionalDouble(String name, Function<A, OptionalDouble> getter) {
203-
return addOptional(name, Structure.DOUBLE, getter.andThen(o -> {
202+
public Function<Container, OptionalInt> addOptionalInt(String name, Function<A, OptionalInt> getter) {
203+
return addOptionalInt(name, Structure.INT, getter);
204+
}
205+
206+
public Function<Container, OptionalDouble> addOptionalDouble(String name, Structure<Double> structure, Function<A, OptionalDouble> getter) {
207+
return addOptional(name, structure, getter.andThen(o -> {
204208
if (o.isPresent()) {
205209
return Optional.of(o.getAsDouble());
206210
}
207211
return Optional.empty();
208212
})).andThen(o -> o.map(OptionalDouble::of).orElse(OptionalDouble.empty()));
209213
}
210214

211-
public Function<Container, OptionalLong> addOptionalLong(String name, Function<A, OptionalLong> getter) {
212-
return addOptional(name, Structure.LONG, getter.andThen(o -> {
215+
public Function<Container, OptionalDouble> addOptionalDouble(String name, Function<A, OptionalDouble> getter) {
216+
return addOptionalDouble(name, Structure.DOUBLE, getter);
217+
}
218+
219+
public Function<Container, OptionalLong> addOptionalLong(String name, Structure<Long> structure, Function<A, OptionalLong> getter) {
220+
return addOptional(name, structure, getter.andThen(o -> {
213221
if (o.isPresent()) {
214222
return Optional.of(o.getAsLong());
215223
}
216224
return Optional.empty();
217225
})).andThen(o -> o.map(OptionalLong::of).orElse(OptionalLong.empty()));
218226
}
219227

228+
public Function<Container, OptionalLong> addOptionalLong(String name, Function<A, OptionalLong> getter) {
229+
return addOptionalLong(name, Structure.LONG, getter);
230+
}
231+
220232
/**
221233
* Add a field to the record structure with a default value. The field will not be encoded if equal to its default value.
222234
* @param name the name of the field

0 commit comments

Comments
 (0)