Skip to content

Commit 721106b

Browse files
authored
80 ts optional types (#81)
* Add configs for ts optional field render format * Pass option to TypescriptInterfaceConverter * Render different optional field formats
1 parent 296be14 commit 721106b

21 files changed

Lines changed: 204 additions & 42 deletions

File tree

annotation/src/main/java/online/sharedtype/SharedType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@
153153
*/
154154
String[] rustMacroTraits() default {};
155155

156+
/**
157+
* How to render optional fields in Typescript.
158+
* @return combination of "?", "null", "undefined", the latter 2 are rendered as union types. If empty, fallback to global default.
159+
*/
160+
String[] typescriptOptionalFieldFormat() default {};
161+
156162
/**
157163
* Mark a method as an accessor regardless of its name.
158164
* Getter prefixes are configured in global properties.

client-test/typescript/src/types.java17.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ export const mapClass: MapClass = {
9191
}
9292
}
9393

94-
export const optionalMethods: OptionalMethod = {};
94+
export const optionalMethods: OptionalMethod = {
95+
valueOptional: undefined,
96+
nestedValueOptional: null,
97+
setNestedValueOptional: null,
98+
mapNestedValueOptional: undefined
99+
};
95100
optionalMethods.mapNestedValueOptional = { 1: "foo" };
96101
optionalMethods.setNestedValueOptional = ["foo"];
97102
optionalMethods.nestedValueOptional = "bar";

it/java8/src/main/java/online/sharedtype/it/java8/OptionalMethod.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
import java.util.Optional;
1010
import java.util.Set;
1111

12-
@SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
12+
@SharedType(
13+
rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"},
14+
typescriptOptionalFieldFormat = {"undefined", "null"}
15+
)
1316
@Setter
1417
public class OptionalMethod {
1518
@SharedType.Ignore

processor/src/main/java/online/sharedtype/processor/context/Config.java

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

33
import lombok.Getter;
44
import online.sharedtype.SharedType;
5+
import online.sharedtype.processor.support.exception.SharedTypeException;
56

67
import javax.lang.model.element.TypeElement;
78
import java.lang.annotation.Retention;
@@ -27,6 +28,8 @@ public final class Config {
2728
private final Set<SharedType.ComponentType> includedComponentTypes;
2829
@Getter
2930
private final boolean constantNamespaced;
31+
@Getter
32+
private final Set<Props.Typescript.OptionalFieldFormat> typescriptOptionalFieldFormats;
3033

3134
@Retention(RetentionPolicy.RUNTIME)
3235
@interface AnnoContainer {
@@ -46,6 +49,7 @@ public Config(TypeElement typeElement, Context ctx) {
4649
List<SharedType.ComponentType> includedCompTypes = Arrays.asList(anno.includes());
4750
this.includedComponentTypes = includedCompTypes.isEmpty() ? Collections.emptySet() : EnumSet.copyOf(includedCompTypes);
4851
constantNamespaced = evaluateOptionalBool(anno.constantNamespaced(), ctx.getProps().isConstantNamespaced());
52+
typescriptOptionalFieldFormats = parseTsOptionalFieldFormats(anno, ctx);
4953
}
5054

5155
public boolean includes(SharedType.ComponentType componentType) {
@@ -62,4 +66,19 @@ private static boolean evaluateOptionalBool(SharedType.OptionalBool optionalBool
6266
return defaultValue;
6367
}
6468
}
69+
70+
private static Set<Props.Typescript.OptionalFieldFormat> parseTsOptionalFieldFormats(SharedType anno, Context ctx) {
71+
if (anno.typescriptOptionalFieldFormat().length > 0) {
72+
EnumSet<Props.Typescript.OptionalFieldFormat> formats = EnumSet.noneOf(Props.Typescript.OptionalFieldFormat.class);
73+
for (String value : anno.typescriptOptionalFieldFormat()) {
74+
try {
75+
formats.add(Props.Typescript.OptionalFieldFormat.fromString(value));
76+
} catch (IllegalArgumentException e) {
77+
throw new SharedTypeException(String.format("Invalid value for SharedType.typescriptOptionalFieldFormat: '%s', only '?', 'null', 'undefined' are allowed.", value), e);
78+
}
79+
}
80+
return formats;
81+
}
82+
return ctx.getProps().getTypescript().getOptionalFieldFormats();
83+
}
6584
}

processor/src/main/java/online/sharedtype/processor/context/Props.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ public static final class Typescript {
3434
private final String outputFileName;
3535
private final char interfacePropertyDelimiter;
3636
private final String javaObjectMapType;
37+
private final Set<OptionalFieldFormat> optionalFieldFormats;
38+
39+
@Getter
40+
public enum OptionalFieldFormat {
41+
QUESTION_MARK("?"),
42+
NULL("null"),
43+
UNDEFINED("undefined"),
44+
;
45+
private final String value;
46+
OptionalFieldFormat(String value) {
47+
this.value = value;
48+
}
49+
public static OptionalFieldFormat fromString(String value) {
50+
for (OptionalFieldFormat format : OptionalFieldFormat.values()) {
51+
if (format.value.equals(value)) {
52+
return format;
53+
}
54+
}
55+
throw new IllegalArgumentException(String.format("Unknown optional field format: '%s', only '?', 'null', 'undefined' are allowed", value));
56+
}
57+
}
3758
}
3859

3960
@Builder(access = AccessLevel.PACKAGE)

processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
import javax.annotation.Nullable;
66
import java.io.InputStream;
7-
import java.lang.annotation.Annotation;
87
import java.nio.file.Files;
98
import java.nio.file.Path;
109
import java.util.EnumSet;
1110
import java.util.LinkedHashSet;
1211
import java.util.Properties;
1312
import java.util.Set;
13+
import java.util.function.Function;
1414

1515
/**
1616
* Global properties loader.
@@ -29,15 +29,19 @@ public static Props loadProps(@Nullable Path userPropertiesFile) {
2929
if (userPropsInputstream != null) {
3030
properties.load(userPropsInputstream);
3131
}
32-
return loadProps(properties);
32+
Props props = loadProps(properties);
33+
if (props.getTypescript().getOptionalFieldFormats().isEmpty()) {
34+
throw new IllegalArgumentException("Props 'typescript.optional-field-format' cannot be empty.");
35+
}
36+
return props;
3337
} catch (Exception e) {
3438
throw new SharedTypeException("Failed to load properties.", e);
3539
}
3640
}
3741

38-
private static Props loadProps(Properties properties) throws Exception {
42+
private static Props loadProps(Properties properties) {
3943
return Props.builder()
40-
.targets(parseEnumSet(properties.getProperty("sharedtype.targets"), OutputTarget.class))
44+
.targets(parseEnumSet(properties, "sharedtype.targets", OutputTarget.class, OutputTarget::valueOf))
4145
.optionalAnnotations(parseClassSet(properties, "sharedtype.optional-annotations"))
4246
.optionalContainerTypes(splitArray(properties.getProperty("sharedtype.optional-container-types")))
4347
.accessorGetterPrefixes(splitArray(properties.getProperty("sharedtype.accessor.getter-prefixes")))
@@ -50,6 +54,8 @@ private static Props loadProps(Properties properties) throws Exception {
5054
.outputFileName(properties.getProperty("sharedtype.typescript.output-file-name"))
5155
.interfacePropertyDelimiter(properties.getProperty("sharedtype.typescript.interface-property-delimiter").charAt(0))
5256
.javaObjectMapType(properties.getProperty("sharedtype.typescript.java-object-map-type"))
57+
.optionalFieldFormats(parseEnumSet(properties,"sharedtype.typescript.optional-field-format",
58+
Props.Typescript.OptionalFieldFormat.class, Props.Typescript.OptionalFieldFormat::fromString))
5359
.build())
5460
.rust(Props.Rust.builder()
5561
.outputFileName(properties.getProperty("sharedtype.rust.output-file-name"))
@@ -60,11 +66,15 @@ private static Props loadProps(Properties properties) throws Exception {
6066
.build();
6167
}
6268

63-
private static <T extends Enum<T>> Set<T> parseEnumSet(String value, Class<T> type) {
64-
Set<String> trimmedElems = splitArray(value);
69+
private static <T extends Enum<T>> Set<T> parseEnumSet(Properties properties, String propertyName, Class<T> type, Function<String, T> enumValueOf) {
70+
Set<String> trimmedElems = splitArray(properties.getProperty(propertyName));
6571
Set<T> set = EnumSet.noneOf(type);
66-
for (String trimmed : trimmedElems) {
67-
set.add(Enum.valueOf(type, trimmed));
72+
try {
73+
for (String trimmed : trimmedElems) {
74+
set.add(enumValueOf.apply(trimmed));
75+
}
76+
} catch (Exception e) {
77+
throw new IllegalArgumentException(String.format("Failed to parse property '%s'", propertyName), e);
6878
}
6979
return set;
7080
}

processor/src/main/java/online/sharedtype/processor/context/TypeStore.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,16 @@
33
import lombok.EqualsAndHashCode;
44
import lombok.RequiredArgsConstructor;
55
import online.sharedtype.processor.domain.ConcreteTypeInfo;
6-
import online.sharedtype.processor.domain.TypeVariableInfo;
7-
import online.sharedtype.processor.parser.type.TypeInfoParser;
86
import online.sharedtype.processor.domain.TypeDef;
97
import online.sharedtype.processor.domain.TypeInfo;
8+
import online.sharedtype.processor.domain.TypeVariableInfo;
9+
import online.sharedtype.processor.parser.type.TypeInfoParser;
1010

1111
import javax.annotation.Nullable;
1212
import java.util.ArrayList;
13-
import java.util.Collections;
1413
import java.util.HashMap;
15-
import java.util.HashSet;
1614
import java.util.List;
1715
import java.util.Map;
18-
import java.util.Set;
19-
import java.util.SortedSet;
20-
import java.util.TreeSet;
2116

2217
import static online.sharedtype.processor.domain.Constants.PREDEFINED_OBJECT_TYPES;
2318

@@ -73,9 +68,12 @@ public boolean containsTypeDef(String qualifiedName) {
7368
public void saveConfig(String qualifiedName, Config config) {
7469
typeConfig.put(qualifiedName, config);
7570
}
76-
@Nullable
77-
public Config getConfig(String qualifiedName) {
78-
return typeConfig.get(qualifiedName);
71+
72+
/**
73+
* @return Config for a typeDef. A typeDef must have a config.
74+
*/
75+
public Config getConfig(TypeDef typeDef) {
76+
return typeConfig.get(typeDef.qualifiedName());
7977
}
8078

8179
@EqualsAndHashCode

processor/src/main/java/online/sharedtype/processor/parser/ClassTypeDefParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private List<TypeInfo> parseSupertypes(TypeElement typeElement, TypeContext type
118118

119119
List<TypeInfo> res = new ArrayList<>(supertypes.size());
120120
for (DeclaredType supertype : supertypes) {
121-
if (!ctx.isIgnored((TypeElement) supertype.asElement())) {
121+
if (!ctx.isIgnored(supertype.asElement())) {
122122
res.add(typeInfoParser.parse(supertype, typeContext));
123123
}
124124
}

processor/src/main/java/online/sharedtype/processor/parser/ConstantTypeDefParser.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,7 @@ public List<TypeDef> parse(TypeElement typeElement) {
5353
return Collections.emptyList();
5454
}
5555

56-
Config config = ctx.getTypeStore().getConfig(qualifiedName);
57-
if (config == null) {
58-
config = new Config(typeElement, ctx);
59-
ctx.getTypeStore().saveConfig(qualifiedName, config);
60-
}
56+
Config config = ctx.getTypeStore().getConfig(mainTypeDef);
6157
if (!config.includes(SharedType.ComponentType.CONSTANTS)) {
6258
return Collections.emptyList();
6359
}

processor/src/main/java/online/sharedtype/processor/writer/converter/AbstractRustConverter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import online.sharedtype.processor.context.Config;
44
import online.sharedtype.processor.context.Context;
55
import online.sharedtype.processor.domain.TypeDef;
6-
import online.sharedtype.processor.support.utils.Utils;
76

87
import java.util.Collections;
98
import java.util.LinkedHashSet;
@@ -19,8 +18,8 @@ abstract class AbstractRustConverter implements TemplateDataConverter {
1918
}
2019

2120
final Set<String> macroTraits(TypeDef typeDef) {
22-
Config config = ctx.getTypeStore().getConfig(typeDef.qualifiedName());
23-
String[] typeMacroTraits = config != null ? config.getAnno().rustMacroTraits() : Utils.emptyStringArray();
21+
Config config = ctx.getTypeStore().getConfig(typeDef);
22+
String[] typeMacroTraits = config.getAnno().rustMacroTraits();
2423
Set<String> traits = new LinkedHashSet<>(typeMacroTraits.length + defaultTraits.size());
2524
traits.addAll(defaultTraits);
2625
Collections.addAll(traits, typeMacroTraits);

0 commit comments

Comments
 (0)