Skip to content

Commit 81da45b

Browse files
committed
restructuring
- split Codec into Encoder and Decoder - removed Codecs, methods are now just in Codec - removed static codec transforms - fixed map-like transforms, matches DFU now - validate now provides a whole result, not just a boolean - added codecDispatch factory
1 parent e877743 commit 81da45b

File tree

20 files changed

+338
-338
lines changed

20 files changed

+338
-338
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55

66
base.archivesName = "TinyCodecs"
77
group = "io.github.cichlidmc"
8-
version = "2.0.0"
8+
version = "3.0.0"
99

1010
repositories {
1111
mavenCentral()
Lines changed: 127 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,181 @@
11
package io.github.cichlidmc.tinycodecs;
22

3+
import io.github.cichlidmc.tinycodecs.codec.ByNameCodec;
4+
import io.github.cichlidmc.tinycodecs.codec.DispatchCodec;
5+
import io.github.cichlidmc.tinycodecs.codec.EitherCodec;
6+
import io.github.cichlidmc.tinycodecs.codec.ListCodec;
7+
import io.github.cichlidmc.tinycodecs.codec.UnitCodec;
8+
import io.github.cichlidmc.tinycodecs.codec.map.CompositeCodec;
9+
import io.github.cichlidmc.tinycodecs.codec.map.FieldOfCodec;
10+
import io.github.cichlidmc.tinycodecs.codec.optional.DefaultedOptionalCodec;
11+
import io.github.cichlidmc.tinycodecs.codec.optional.OptionalCodec;
12+
import io.github.cichlidmc.tinycodecs.map.MapCodec;
313
import io.github.cichlidmc.tinycodecs.util.Either;
14+
import io.github.cichlidmc.tinyjson.JsonException;
415
import io.github.cichlidmc.tinyjson.value.JsonValue;
16+
import io.github.cichlidmc.tinyjson.value.primitive.JsonBool;
17+
import io.github.cichlidmc.tinyjson.value.primitive.JsonNumber;
18+
import io.github.cichlidmc.tinyjson.value.primitive.JsonString;
19+
import org.jetbrains.annotations.Nullable;
520

21+
import java.util.Collections;
22+
import java.util.HashMap;
623
import java.util.List;
24+
import java.util.Map;
725
import java.util.Optional;
826
import java.util.function.Function;
9-
import java.util.function.Predicate;
1027
import java.util.function.Supplier;
11-
import java.util.function.UnaryOperator;
1228

1329
/**
14-
* A Codec determines how an object is (de)serialized.
15-
* It accepts and outputs any type of JSON.
30+
* A Codec is both an encoder and decoder of some type of object.
31+
* @see CompositeCodec
1632
*/
17-
public interface Codec<T> {
18-
/**
19-
* Attempt to decode the given JsonValue.
20-
* Should never throw an exception.
21-
* @param value the JSON to decode. A JsonNull may indicate either a literal null or a missing field
22-
*/
23-
CodecResult<T> decode(JsonValue value);
24-
25-
/**
26-
* Attempt to encode the given value to JSON.
27-
* Should never throw an exception.
28-
*/
29-
CodecResult<? extends JsonValue> encode(T value);
33+
public interface Codec<T> extends Encoder<T>, Decoder<T> {
34+
// base implementations
35+
Codec<Boolean> BOOL = throwing(json -> json.asBoolean().value(), JsonBool::new);
36+
Codec<String> STRING = throwing(json -> json.asString().value(), JsonString::new);
37+
Codec<Byte> BYTE = number(Number::byteValue);
38+
Codec<Short> SHORT = number(Number::shortValue);
39+
Codec<Integer> INT = number(Number::intValue);
40+
Codec<Long> LONG = number(Number::longValue);
41+
Codec<Float> FLOAT = number(Number::floatValue);
42+
Codec<Double> DOUBLE = number(Number::doubleValue);
3043

3144
// transforms
32-
33-
default Codec<T> validate(Predicate<T> validator) {
34-
return Codecs.validate(this, validator);
35-
}
3645

37-
default Codec<T> map(UnaryOperator<T> function) {
38-
return Codecs.map(this, function);
46+
default Codec<T> validate(Function<? super T, ? extends CodecResult<T>> validator) {
47+
return this.flatXmap(validator, validator);
3948
}
4049

41-
default Codec<T> mapResult(UnaryOperator<CodecResult<T>> function) {
42-
return Codecs.mapResult(this, function);
50+
default <B> Codec<B> xmap(Function<? super T, ? extends B> to, Function<? super B, ? extends T> from) {
51+
return Codec.of(this.comap(from), this.map(to));
4352
}
4453

45-
default Codec<T> flatMap(Function<T, CodecResult<T>> function) {
46-
return Codecs.flatMap(this, function);
54+
default <B> Codec<B> comapFlatMap(Function<? super T, ? extends CodecResult<? extends B>> to, Function<? super B, ? extends T> from) {
55+
return Codec.of(this.comap(from), this.flatMap(to));
4756
}
4857

49-
default <B> Codec<B> xmap(Function<? super T, ? extends B> aToB, Function<? super B, ? extends T> bToA) {
50-
return Codecs.xmap(this, aToB, bToA);
58+
default <B> Codec<B> flatComapMap(Function<? super T, ? extends B> to, Function<? super B, ? extends CodecResult<? extends T>> from) {
59+
return Codec.of(this.flatComap(from), this.map(to));
5160
}
5261

53-
default <B> Codec<B> flatXmap(Function<T, CodecResult<B>> aToB, Function<B, T> bToA) {
54-
return Codecs.flatXmap(this, aToB, bToA);
62+
default <B> Codec<B> flatXmap(Function<? super T, ? extends CodecResult<? extends B>> to, Function<? super B, ? extends CodecResult<? extends T>> from) {
63+
return Codec.of(this.flatComap(from), this.flatMap(to));
5564
}
5665

5766
default <B> Codec<B> dispatch(String key, Function<? super B, ? extends T> typeGetter, Function<? super T, ? extends MapCodec<? extends B>> codecGetter) {
58-
return Codecs.dispatch(this, key, typeGetter, codecGetter);
67+
return new DispatchCodec<>(this, key, typeGetter, codecGetter);
5968
}
6069

6170
default <B> Codec<B> dispatch(Function<? super B, ? extends T> typeGetter, Function<? super T, MapCodec<? extends B>> codecGetter) {
62-
return Codecs.dispatch(this, typeGetter, codecGetter);
71+
return this.dispatch("type", typeGetter, codecGetter);
6372
}
6473

6574
default Codec<T> withAlternative(Codec<? extends T> alternative) {
66-
return Codecs.withAlternative(this, alternative);
75+
return this.either(alternative).xmap(Either::merge, Either::left);
6776
}
6877

6978
default Codec<List<T>> listOf() {
70-
return Codecs.listOf(this);
79+
return new ListCodec<>(this);
7180
}
7281

7382
default Codec<List<T>> listOrSingle() {
74-
return Codecs.listOrSingle(this);
83+
return this.listOf().withAlternative(this.flatComapMap(
84+
Collections::singletonList,
85+
list -> list.size() == 1 ? CodecResult.success(list.get(0)) : CodecResult.error("Not a singleton")
86+
));
7587
}
7688

7789
default Codec<Optional<T>> optional() {
78-
return Codecs.optional(this);
90+
return new OptionalCodec<>(this);
7991
}
8092

8193
default Codec<T> optional(Supplier<T> fallback) {
82-
return Codecs.optional(this, fallback);
94+
return new DefaultedOptionalCodec<>(this, fallback);
8395
}
8496

8597
default Codec<T> optional(T fallback) {
86-
return Codecs.optional(this, fallback);
98+
return this.optional(() -> fallback);
99+
}
100+
101+
default <B> Codec<Either<T, B>> either(Codec<B> other) {
102+
return new EitherCodec<>(this, other, false);
87103
}
88104

89-
default <B> Codec<Either<T, B>> xor(Codec<B> otherCodec) {
90-
return Codecs.xor(this, otherCodec);
105+
default <B> Codec<Either<T, B>> xor(Codec<B> other) {
106+
return new EitherCodec<>(this, other, true);
91107
}
92108

93109
default MapCodec<T> fieldOf(String name) {
94-
return Codecs.fieldOf(this, name);
110+
return new FieldOfCodec<>(this, name);
111+
}
112+
113+
// factories
114+
115+
static <T> Codec<T> of(Encoder<? super T> encoder, Decoder<T> decoder) {
116+
return new Codec<T>() {
117+
@Override
118+
public CodecResult<T> decode(JsonValue json) {
119+
return decoder.decode(json);
120+
}
121+
122+
@Override
123+
public CodecResult<? extends JsonValue> encode(T value) {
124+
return encoder.encode(value);
125+
}
126+
};
127+
}
128+
129+
/**
130+
* Create a codec from a function that may throw a {@link JsonException}.
131+
*/
132+
static <T> Codec<T> throwing(Function<? super JsonValue, ? extends T> decoder, Function<? super T, ? extends JsonValue> encoder) {
133+
return new Codec<T>() {
134+
@Override
135+
public CodecResult<T> decode(JsonValue json) {
136+
try {
137+
return CodecResult.success(decoder.apply(json));
138+
} catch (JsonException e) {
139+
return CodecResult.error(e.getMessage());
140+
}
141+
}
142+
143+
@Override
144+
public CodecResult<? extends JsonValue> encode(T value) {
145+
return CodecResult.success(encoder.apply(value));
146+
}
147+
};
148+
}
149+
150+
static <T extends Number> Codec<T> number(Function<Number, T> decoder) {
151+
return throwing(
152+
json -> decoder.apply(json.asNumber().strictValue()),
153+
number -> new JsonNumber(number.doubleValue())
154+
);
155+
}
156+
157+
static <T> Codec<T> byName(Function<? super T, @Nullable String> nameGetter, Function<? super String, ? extends @Nullable T> byName) {
158+
return new ByNameCodec<>(nameGetter, byName);
159+
}
160+
161+
static <T extends Enum<T>> Codec<T> byName(Class<T> clazz, Function<T, String> nameGetter) {
162+
T[] values = clazz.getEnumConstants();
163+
Map<String, T> byName = new HashMap<>();
164+
for (T value : values) {
165+
byName.put(nameGetter.apply(value), value);
166+
}
167+
return byName(nameGetter, byName::get);
168+
}
169+
170+
static <T extends Enum<T>> Codec<T> byName(Class<T> clazz) {
171+
return byName(clazz, Enum::name);
172+
}
173+
174+
static <T> Codec<T> unit(T unit) {
175+
return new UnitCodec<>(unit);
176+
}
177+
178+
static <T> Codec<T> codecDispatch(Codec<MapCodec<? extends T>> codec, Function<? super T, ? extends MapCodec<? extends T>> getter) {
179+
return codec.dispatch(getter, Function.identity());
95180
}
96181
}

0 commit comments

Comments
 (0)