referencingTypes() {
21 | return referencingTypes;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/domain/type/TypeInfo.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.type;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 |
5 | import java.io.Serializable;
6 | import java.util.Map;
7 |
8 | /**
9 | * Type information. Similar concept as {@link javax.lang.model.type.TypeMirror}.
10 | *
11 | * @author Cause Chung
12 | * @see TypeDef
13 | */
14 | public interface TypeInfo extends Serializable {
15 | /**
16 | * Check if this type and its dependency types are resolved.
17 | *
18 | * When parsing a type's structure, a dependency type is firstly captured as a {@link TypeInfo}.
19 | * At this stage, because we don't know its output structure or if it needs to be output at all, we mark it as unresolved.
20 | * Also, due to possible cyclic dependencies, the resolution stage needs to be performed after initial parsing state.
21 | * During resolution, once a type is parsed, it's marked as resolved.
22 | * Note that a type is marked as resolved when created, if it can be determined at that time.
23 | *
24 | * Object contains resolved flag as a mutable state
25 | *
26 | * @return true is this type and its dependency types are resolved.
27 | */
28 | default boolean resolved() {
29 | return true;
30 | }
31 |
32 | /**
33 | * Replace type variables with type arguments.
34 | *
35 | * @param mappings key is a type variable e.g. T
36 | * value is a type argument, a concrete type e.g. Integer, or a generic type with concrete type parameter, e.g. {@code Tuple}
37 | * @return a newly created type info if updated.
38 | */
39 | default TypeInfo reify(Map mappings) {
40 | return this;
41 | }
42 |
43 | /**
44 | * Mark this type as referenced by another type. Used for e.g. cyclic reference detection.
45 | *
46 | * @param typeDef type that references this type
47 | */
48 | void addReferencingType(TypeDef typeDef);
49 |
50 | /**
51 | * Represents no type. This can happen when a type is not visible, e.g. not on module path.
52 | * @see online.sharedtype.processor.parser.type.TypeInfoParser
53 | */
54 | TypeInfo NO_TYPE_INFO = NoTypeInfo.INSTANCE;
55 | }
56 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/domain/type/TypeVariableInfo.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.type;
2 |
3 | import lombok.Builder;
4 | import lombok.EqualsAndHashCode;
5 |
6 | import java.util.Map;
7 |
8 | /**
9 | * Represents a generic type variable.
10 | *
11 | * A type variable refers to a generic type parameter, it has a notation like "T" or bound information like "T extends Number".
12 | * A type argument is the actual type of the type variable. E.g. {@code "Integer" in "List"}.
13 | *
14 | * @see ConcreteTypeInfo#typeArgs()
15 | * @author Cause Chung
16 | */
17 | @EqualsAndHashCode(of = {"contextTypeQualifiedName", "name"}, callSuper = false)
18 | @Builder
19 | public final class TypeVariableInfo extends ReferableTypeInfo {
20 | private static final long serialVersionUID = 7632941203572660271L;
21 | private final String contextTypeQualifiedName; // TODO: reference to TypeDef to avoid string
22 | private final String name;
23 | private String qualifiedName;
24 | // TODO: support generic bounds
25 |
26 | public static String concatQualifiedName(String contextTypeQualifiedName, String name) {
27 | return contextTypeQualifiedName + "@" + name;
28 | }
29 |
30 | public String contextTypeQualifiedName() {
31 | return contextTypeQualifiedName;
32 | }
33 |
34 | public String name() {
35 | return name;
36 | }
37 |
38 | public String qualifiedName() {
39 | if (qualifiedName == null) {
40 | qualifiedName = concatQualifiedName(contextTypeQualifiedName, name);
41 | }
42 | return qualifiedName;
43 | }
44 |
45 | @Override
46 | public TypeInfo reify(Map mappings) {
47 | TypeInfo reifiedType = mappings.get(this);
48 | return reifiedType == null ? this : reifiedType;
49 | }
50 |
51 | @Override
52 | public String toString() {
53 | return name;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/domain/value/EnumConstantValue.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.value;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.Getter;
5 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
6 | import online.sharedtype.processor.domain.type.TypeInfo;
7 |
8 | @Getter
9 | @EqualsAndHashCode(callSuper = true)
10 | public final class EnumConstantValue extends LiteralValue {
11 | private static final long serialVersionUID = -6711930218877737970L;
12 | private final ConcreteTypeInfo enumType;
13 | private final String enumConstantName;
14 | EnumConstantValue(ConcreteTypeInfo enumType, String enumConstantName, ConcreteTypeInfo valueType, Object value) {
15 | super(valueType, value);
16 | this.enumType = enumType;
17 | this.enumConstantName = enumConstantName;
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return String.format("%s(%s)", enumConstantName, getValue());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/domain/value/LiteralValue.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.value;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.EqualsAndHashCode;
5 | import lombok.Getter;
6 | import lombok.RequiredArgsConstructor;
7 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
8 | import online.sharedtype.processor.domain.type.TypeInfo;
9 |
10 | import java.util.Objects;
11 |
12 | @Getter
13 | @EqualsAndHashCode
14 | @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
15 | public class LiteralValue implements ValueHolder {
16 | private static final long serialVersionUID = -7324230239169028973L;
17 | private final ConcreteTypeInfo valueType;
18 | private final Object value;
19 |
20 | @Override
21 | public String toString() {
22 | return Objects.toString(value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/domain/value/ValueHolder.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.value;
2 |
3 | import online.sharedtype.processor.domain.Constants;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 |
6 | import java.io.Serializable;
7 |
8 | public interface ValueHolder extends Serializable {
9 | ConcreteTypeInfo getValueType();
10 | Object getValue();
11 |
12 | default String literalValue() {
13 | Object value = getValue();
14 | if (value instanceof CharSequence || value instanceof Character) {
15 | return String.format("\"%s\"", value); // TODO: options single or double quotes?
16 | } else {
17 | return String.valueOf(value);
18 | }
19 | }
20 |
21 | static ValueHolder of(ConcreteTypeInfo valueType, Object value) {
22 | if (value instanceof ValueHolder) {
23 | return (ValueHolder) value;
24 | } else {
25 | return new LiteralValue(valueType, value);
26 | }
27 | }
28 |
29 | static EnumConstantValue ofEnum(ConcreteTypeInfo enumType, String enumConstantName, ConcreteTypeInfo valueType, Object value) {
30 | ConcreteTypeInfo actualValueType = valueType;
31 | Object actualValue = value;
32 | while (actualValue instanceof ValueHolder) {
33 | ValueHolder valueHolder = (ValueHolder) actualValue;
34 | actualValueType = valueHolder.getValueType();
35 | actualValue = valueHolder.getValue();
36 | }
37 | return new EnumConstantValue(enumType, enumConstantName, actualValueType, actualValue);
38 | }
39 |
40 | LiteralValue NULL = new LiteralValue(Constants.NULL_TYPE_INFO,null);
41 | }
42 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/support/annotation/Issue.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.annotation;
2 |
3 |
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 |
7 | /**
8 | * Mark an issue number.
9 | *
10 | * @author Cause Chung
11 | */
12 | @Retention(RetentionPolicy.SOURCE)
13 | public @interface Issue {
14 | int value();
15 | String comment() default "";
16 | }
17 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/support/annotation/Nullable.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.annotation;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * Nullable. To avoid jsr305 dependency.
11 | * @author Cause Chung
12 | */
13 | @Documented
14 | @Retention(RetentionPolicy.RUNTIME)
15 | @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER, ElementType.PARAMETER, ElementType.FIELD})
16 | public @interface Nullable {
17 | }
18 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/support/annotation/SideEffect.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Indicate side effect performed by a method or on a parameter.
10 | *
11 | * @author Cause Chung
12 | */
13 | @Target({ElementType.METHOD, ElementType.PARAMETER})
14 | @Retention(RetentionPolicy.SOURCE)
15 | public @interface SideEffect {
16 | String value() default "";
17 | }
18 |
--------------------------------------------------------------------------------
/internal/src/main/java/online/sharedtype/processor/support/annotation/VisibleForTesting.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Indicate that the visibility of a method or class is greater than needed in compile scope code, but exposed for testing purpose.
10 | *
11 | * @author Cause Chung
12 | */
13 | @Target({ ElementType.METHOD, ElementType.TYPE })
14 | @Retention(RetentionPolicy.SOURCE)
15 | public @interface VisibleForTesting {
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/internal/src/test/java/online/sharedtype/it/support/TypeDefDeserializer.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.support;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.ObjectInputStream;
8 |
9 | import static java.util.Objects.requireNonNull;
10 |
11 | public final class TypeDefDeserializer {
12 | private static final ClassLoader classLoader = TypeDefDeserializer.class.getClassLoader();
13 | private TypeDefDeserializer() {}
14 |
15 | public static TypeDef deserializeTypeDef(String serFilename) {
16 | try (InputStream is = classLoader.getResourceAsStream(serFilename);
17 | ObjectInputStream ois = new ObjectInputStream(requireNonNull(is, "Cannot find " + serFilename))) {
18 | return (TypeDef) ois.readObject();
19 | } catch (IOException | ClassNotFoundException e) {
20 | throw new RuntimeException(e);
21 | }
22 | }
23 |
24 | public static boolean doesResourceExist(String serFilename) {
25 | return classLoader.getResource(serFilename) != null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/it/custom-code.go:
--------------------------------------------------------------------------------
1 | // some custom code for go
2 |
3 |
--------------------------------------------------------------------------------
/it/custom-code.rs:
--------------------------------------------------------------------------------
1 | // test code snippet
2 | pub struct CustomInjectedStruct {
3 | pub field: i32,
4 | }
5 |
6 | fn serialize_any(value: &Box, serializer: S) -> Result
7 | where
8 | S: serde::Serializer,
9 | {
10 | if let Some(value) = value.downcast_ref::() {
11 | serializer.serialize_str(value)
12 | } else if let Some(value) = value.downcast_ref::() {
13 | serializer.serialize_i32(*value)
14 | } else {
15 | Err(serde::ser::Error::custom("Unsupported type"))
16 | }
17 | }
18 | fn deserialize_any<'de, D>(deserializer: D) -> Result, D::Error>
19 | where
20 | D: serde::Deserializer<'de>,
21 | {
22 | let s: &str = serde::Deserialize::deserialize(deserializer)?;
23 | Ok(Box::new(s.to_string()))
24 | }
25 |
--------------------------------------------------------------------------------
/it/custom-code.ts:
--------------------------------------------------------------------------------
1 | // test code snippet
2 | export interface CustomCode {
3 | }
4 |
--------------------------------------------------------------------------------
/it/java17/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | online.sharedtype
6 | sharedtype-it-parent
7 | 0.12.1-SNAPSHOT
8 | ../pom.xml
9 |
10 |
11 | sharedtype-it-java17
12 | SharedType Integration Test Java17
13 |
14 |
15 | 21
16 | 21
17 |
18 |
19 |
--------------------------------------------------------------------------------
/it/java17/src/main/java/online/sharedtype/it/java17/JavaRecord.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java17;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnore;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import lombok.Builder;
6 | import online.sharedtype.SharedType;
7 | import online.sharedtype.it.java8.Container;
8 | import online.sharedtype.it.java8.DependencyClassA;
9 | import online.sharedtype.it.java8.EnumGalaxy;
10 | import online.sharedtype.it.java8.EnumSize;
11 | import online.sharedtype.it.java8.InterfaceA;
12 |
13 | import java.util.Collection;
14 | import java.util.List;
15 | import java.util.Set;
16 |
17 | @SharedType(
18 | constantNamespaced = SharedType.OptionalBool.FALSE,
19 | includes = {
20 | SharedType.ComponentType.CONSTANTS,
21 | SharedType.ComponentType.FIELDS,
22 | SharedType.ComponentType.ACCESSORS,
23 | },
24 | rustMacroTraits = {"serde::Serialize", "serde::Deserialize"}
25 | )
26 | @Builder
27 | public record JavaRecord(
28 | String string,
29 | byte primitiveByte,
30 | Byte boxedByte,
31 | short primitiveShort,
32 | Short boxedShort,
33 | int primitiveInt,
34 | Integer boxedInt,
35 | long primitiveLong,
36 | Long boxedLong,
37 | float primitiveFloat,
38 | Float boxedFloat,
39 | double primitiveDouble,
40 | Double boxedDouble,
41 | boolean primitiveBoolean,
42 | Boolean boxedBoolean,
43 | char primitiveChar,
44 | Character boxedChar,
45 | @SharedType.TagLiteral(tags = "// test comments for class")
46 | @SharedType.TagLiteral(tags = "#[serde(serialize_with = \"serialize_any\", deserialize_with = \"deserialize_any\")]", targets = SharedType.TargetType.RUST)
47 | Object object,
48 | // Void aVoid,
49 |
50 | DependencyClassA cyclicDependency,// cyclic a ->b ->c ->a
51 |
52 | List> containerStringList,
53 | List>> containerStringListCollection,
54 |
55 | List genericList,
56 | Set genericSet,
57 | List> genericListSet,
58 | // Map genericMap,
59 | int[] intArray,
60 | Integer[] boxedIntArray,
61 |
62 | EnumGalaxy enumGalaxy,
63 | EnumSize enumSize,
64 |
65 | String duplicateAccessor,
66 | @SharedType.Ignore @JsonIgnore String explicitlyIgnored,
67 | T value
68 | ) implements InterfaceA {
69 | static final int STATIC_FIELD_FROM_JAVA_RECORD = 888;
70 |
71 | @SharedType.Accessor
72 | String getDuplicateAccessor() {
73 | return duplicateAccessor;
74 | }
75 |
76 | String shouldNotBeIncluded() {
77 | return null;
78 | }
79 |
80 | @JsonProperty(access = JsonProperty.Access.READ_WRITE)
81 | @Override
82 | public T getValue() {
83 | return value;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/it/java17/src/main/java/online/sharedtype/it/java8:
--------------------------------------------------------------------------------
1 | ../../../../../../../java8/src/main/java/online/sharedtype/it/java8
--------------------------------------------------------------------------------
/it/java17/src/main/java/online/sharedtype/it/java8_is_symlink:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SharedType/sharedtype/66eea847fb581dcde5ada2e886a1eca92871db81/it/java17/src/main/java/online/sharedtype/it/java8_is_symlink
--------------------------------------------------------------------------------
/it/java17/src/test/java/online/sharedtype/it/CustomAnnoClassIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.def.EnumDef;
5 | import org.junit.jupiter.api.Test;
6 |
7 | import static online.sharedtype.it.support.TypeDefDeserializer.deserializeTypeDef;
8 | import static org.assertj.core.api.Assertions.assertThat;
9 |
10 | final class CustomAnnoClassIntegrationTest {
11 | @Test
12 | void customAnnoClass() {
13 | ClassDef classDef = (ClassDef)deserializeTypeDef("online.sharedtype.it.java8.anno.CustomAnnoClass.ser");
14 | assertThat(classDef.simpleName()).isEqualTo("CustomAnnoClass");
15 | assertThat(classDef.components()).hasSize(1);
16 | var field1 = classDef.components().get(0);
17 | assertThat(field1.name()).isEqualTo("someValue");
18 | }
19 |
20 | @Test
21 | void customAnnoEnum() {
22 | EnumDef enumDef = (EnumDef)deserializeTypeDef("online.sharedtype.it.java8.anno.CustomAnnoEnum.ser");
23 | assertThat(enumDef.simpleName()).isEqualTo("CustomAnnoEnum");
24 | assertThat(enumDef.components()).hasSize(2);
25 | var field1 = enumDef.components().get(0);
26 | assertThat(field1.name()).isEqualTo("A");
27 | assertThat(field1.value().getValue()).isEqualTo(1);
28 | var field2 = enumDef.components().get(1);
29 | assertThat(field2.name()).isEqualTo("B");
30 | assertThat(field2.value().getValue()).isEqualTo(2);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/it/java17/src/test/java/online/sharedtype/it/JsonSerializationTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
5 | import online.sharedtype.it.java17.JavaRecord;
6 | import online.sharedtype.it.java8.Container;
7 | import online.sharedtype.it.java8.DependencyClassA;
8 | import online.sharedtype.it.java8.DependencyClassB;
9 | import online.sharedtype.it.java8.DependencyClassC;
10 | import online.sharedtype.it.java8.EnumGalaxy;
11 | import online.sharedtype.it.java8.EnumSize;
12 | import online.sharedtype.it.java8.OptionalMethod;
13 | import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
14 | import org.junit.jupiter.api.Test;
15 | import org.junit.jupiter.api.TestInstance;
16 |
17 | import java.util.List;
18 | import java.util.Set;
19 |
20 | import static org.assertj.core.api.Assertions.assertThat;
21 |
22 | @TestInstance(TestInstance.Lifecycle.PER_CLASS)
23 | final class JsonSerializationTest {
24 | private final ObjectMapper objectMapper = new ObjectMapper();
25 | {
26 | objectMapper.registerModule(new Jdk8Module());
27 | }
28 |
29 | @Test
30 | void jsonOptionalMethod() throws Exception {
31 | var obj = new OptionalMethod();
32 | obj.setValue("value");
33 | var json = objectMapper.writeValueAsString(obj);
34 | System.out.println(json);
35 | }
36 |
37 | @Test
38 | void test() throws Exception {
39 | var obj = JavaRecord
40 | .builder()
41 | .build();
42 | var json = objectMapper.writeValueAsString(obj);
43 | System.out.println(json);
44 | var deser = objectMapper.readValue(json, JavaRecord.class);
45 | assertThat(deser).usingRecursiveComparison(RecursiveComparisonConfiguration.builder().withIgnoredFields("explicitlyIgnored").build())
46 | .isEqualTo(obj);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/it/java17/src/test/java/online/sharedtype/it/OptionalTypeIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it;
2 |
3 | import online.sharedtype.processor.domain.type.ArrayTypeInfo;
4 | import online.sharedtype.processor.domain.def.ClassDef;
5 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
6 | import online.sharedtype.processor.domain.Constants;
7 | import org.junit.jupiter.api.Test;
8 |
9 | import static online.sharedtype.it.support.TypeDefDeserializer.deserializeTypeDef;
10 | import static org.assertj.core.api.Assertions.assertThat;
11 |
12 | final class OptionalTypeIntegrationTest {
13 | @Test
14 | void optionalMethodClass() {
15 | ClassDef classDef = (ClassDef)deserializeTypeDef("online.sharedtype.it.java8.OptionalMethod.ser");
16 | assertThat(classDef.simpleName()).isEqualTo("OptionalMethod");
17 | assertThat(classDef.components()).satisfiesExactly(
18 | field1 -> {
19 | assertThat(field1.name()).isEqualTo("valueOptional");
20 | assertThat(field1.optional()).isTrue();
21 | assertThat(field1.type()).isEqualTo(Constants.STRING_TYPE_INFO);
22 | },
23 | field2 -> {
24 | assertThat(field2.name()).isEqualTo("nestedValueOptional");
25 | assertThat(field2.optional()).isTrue();
26 | assertThat(field2.type()).isEqualTo(Constants.STRING_TYPE_INFO);
27 | },
28 | field3 -> {
29 | assertThat(field3.name()).isEqualTo("setNestedValueOptional");
30 | assertThat(field3.optional()).isTrue();
31 | ArrayTypeInfo arrayTypeInfo = (ArrayTypeInfo) field3.type();
32 | assertThat(arrayTypeInfo.component()).isEqualTo(Constants.STRING_TYPE_INFO);
33 | },
34 | field4 -> {
35 | assertThat(field4.name()).isEqualTo("mapNestedValueOptional");
36 | assertThat(field4.optional()).isTrue();
37 | ConcreteTypeInfo fieldTypeInfo = (ConcreteTypeInfo) field4.type();
38 | assertThat(fieldTypeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
39 | assertThat(fieldTypeInfo.qualifiedName()).isEqualTo("java.util.Map");
40 | assertThat(fieldTypeInfo.typeArgs()).hasSize(2).satisfiesExactly(
41 | keyType -> assertThat(keyType).isEqualTo(Constants.BOXED_INT_TYPE_INFO),
42 | valueType -> assertThat(valueType).isEqualTo(Constants.STRING_TYPE_INFO)
43 | );
44 | }
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/it/java8/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | online.sharedtype
6 | sharedtype-it-parent
7 | 0.12.1-SNAPSHOT
8 | ../pom.xml
9 |
10 |
11 | sharedtype-it-java8
12 | SharedType Integration Test Java8
13 |
14 |
15 | ${java.version}
16 | ${java.version}
17 | ${java.version}
18 | ${java.version}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | it-no-jpms
30 |
31 | true
32 |
33 |
34 | 8
35 | module-info.java
36 |
37 |
38 |
39 | it-jpms
40 |
41 | 9
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/it/java8/setenv:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export JAVA_HOME=$JAVA8_HOME
4 | export PATH=$JAVA_HOME/bin:$PATH
5 |
6 | java -version
7 | alias mvnw="../../mvnw"
8 | echo "alias mvnw is point to ../../mvnw"
9 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module sharedtype.it {
2 | requires java.compiler;
3 | requires java.base;
4 | requires java.sql;
5 | requires online.sharedtype.annotation;
6 | requires com.fasterxml.jackson.annotation;
7 | requires org.joda.time;
8 |
9 | requires static lombok;
10 | }
11 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/ArrayClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(
7 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"}
8 | )
9 | @Data
10 | public final class ArrayClass {
11 | private CustomList arr;
12 | }
13 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/Container.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(
7 | rustMacroTraits = {"serde::Serialize", "serde::Deserialize"}
8 | )
9 | @Data
10 | public final class Container {
11 | private T t;
12 | }
13 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/CustomList.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import java.util.ArrayList;
4 |
5 | public final class CustomList extends ArrayList {
6 | private static final long serialVersionUID = 5138328998123758779L;
7 | }
8 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/CustomMap.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import java.util.HashMap;
4 |
5 | public final class CustomMap extends HashMap {
6 | private static final long serialVersionUID = 2346546437868922578L;
7 | }
8 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/DependencyClassA.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import lombok.EqualsAndHashCode;
5 | import online.sharedtype.SharedType;
6 |
7 | @SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
8 | @Data
9 | @EqualsAndHashCode(callSuper = true)
10 | public final class DependencyClassA extends SuperClassA {
11 | private DependencyClassB b;
12 | }
13 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/DependencyClassB.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
7 | @Data
8 | public final class DependencyClassB {
9 | private DependencyClassC c;
10 | }
11 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/DependencyClassC.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
7 | @Data
8 | public final class DependencyClassC {
9 | private DependencyClassA a;
10 | }
11 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/EnumGalaxy.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(
7 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"}
8 | )
9 | public enum EnumGalaxy {
10 | MilkyWay, Andromeda, Triangulum;
11 | }
12 |
13 | @RequiredArgsConstructor
14 | @SharedType
15 | enum EnumConstReference {
16 | ReferenceConstantInOther(MyConstants.LONG_VALUE),
17 | ReferenceConstantLocally(EnumConstReference.LONG_VALUE),
18 | ;
19 | private static final long LONG_VALUE = 156L;
20 | @SharedType.EnumValue
21 | private final long longValue;
22 | }
23 |
24 | @RequiredArgsConstructor
25 | @SharedType
26 | enum EnumEnumReference {
27 | ReferenceAnother(EnumSize.LARGE),
28 | ;
29 | @SharedType.EnumValue
30 | private final EnumSize enumValue;
31 | }
32 |
33 | @RequiredArgsConstructor
34 | @SharedType
35 | enum EnumSimpleEnumReference {
36 | ReferenceAnother1(EnumGalaxy.Andromeda),
37 | ;
38 | @SharedType.EnumValue
39 | private final EnumGalaxy enumValue;
40 | }
41 |
42 | @RequiredArgsConstructor
43 | @SharedType
44 | enum EnumEnumEnumReference {
45 | ReferenceAnother2(EnumSimpleEnumReference.ReferenceAnother1),
46 | ;
47 | @SharedType.EnumValue
48 | private final EnumSimpleEnumReference enumValue;
49 | }
50 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/EnumSize.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import com.fasterxml.jackson.annotation.JsonValue;
4 | import lombok.RequiredArgsConstructor;
5 | import online.sharedtype.SharedType;
6 |
7 | @SharedType(
8 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"},
9 | typescriptEnumFormat = "const_enum",
10 | goEnumFormat = "struct"
11 | )
12 | @RequiredArgsConstructor
13 | public enum EnumSize {
14 | SMALL(1),
15 | MEDIUM(2),
16 | @SharedType.TagLiteral(tags = "// test comments for enum")
17 | LARGE(3);
18 |
19 | @JsonValue
20 | @SharedType.EnumValue
21 | private final int size;
22 | }
23 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/EnumTShirt.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Getter;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType(typescriptEnumFormat = "enum")
7 | @Getter
8 | public enum EnumTShirt {
9 | S(EnumSize.SMALL, "S"),
10 | M(EnumSize.MEDIUM, "M"),
11 | L(EnumSize.LARGE, "L"),
12 | ;
13 |
14 | private final EnumSize size;
15 | @SharedType.EnumValue
16 | private final String name;
17 |
18 | EnumTShirt(EnumSize size, String name) {
19 | this.size = size;
20 | this.name = name;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/IgnoredInterfaceB.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnore;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType.Ignore
7 | interface IgnoredInterfaceB {
8 | @JsonIgnore
9 | default boolean getBooleanValue() {
10 | return false;
11 | }
12 |
13 | int getNotIgnoredImplementedMethod();
14 | }
15 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/IgnoredSuperClassB.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | @SharedType.Ignore
6 | abstract class IgnoredSuperClassB {
7 | static final long LONG_VALUE_IN_SUPER = 345L;
8 | int field;
9 | }
10 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/InterfaceA.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | public interface InterfaceA {
4 | T getValue();
5 | }
6 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/InterfaceSubA.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | public interface InterfaceSubA extends InterfaceA {
4 | }
5 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/JavaClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import online.sharedtype.SharedType;
7 |
8 | @EqualsAndHashCode(callSuper = true)
9 | @Setter
10 | @SharedType(
11 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"}
12 | )
13 | public final class JavaClass extends SuperClassA {
14 | static final long SOME_LONG_VALUE = 123L;
15 |
16 | @Getter
17 | private String string;
18 | @Getter
19 | private EnumSize size;
20 | // private IgnoredInterfaceB b; // compilation failure
21 | private @SharedType.Ignore IgnoredInterfaceB ignoredB;
22 |
23 | @Override
24 | public int getNotIgnoredImplementedMethod() {
25 | return 1;
26 | }
27 |
28 | @SharedType
29 | static class InnerClass {
30 | private int value;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/JavaTimeClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import lombok.Data;
5 | import online.sharedtype.SharedType;
6 |
7 | import java.time.LocalDate;
8 | import java.time.LocalDateTime;
9 | import java.time.LocalTime;
10 | import java.time.OffsetDateTime;
11 | import java.time.OffsetTime;
12 | import java.time.ZonedDateTime;
13 |
14 | @SharedType(
15 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"},
16 | typescriptTargetDatetimeTypeLiteral = "Date"
17 | )
18 | @Data
19 | public final class JavaTimeClass {
20 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
21 | java.util.Date utilDate;
22 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
23 | java.sql.Date sqlDate;
24 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
25 | LocalDate localDate;
26 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss.SSSSSSSSS")
27 | LocalTime localTime;
28 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS")
29 | LocalDateTime localDateTime;
30 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
31 | ZonedDateTime zonedDateTime;
32 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
33 | OffsetDateTime offsetDateTime;
34 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss.SSSSSSSSSXXX")
35 | OffsetTime offsetTime;
36 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX", timezone = "UTC")
37 | java.time.Instant instant;
38 | }
39 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/JodaTimeClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | @SharedType
6 | final class JodaTimeClass {
7 | org.joda.time.DateTime jodaDateTime;
8 | org.joda.time.LocalDate jodaLocalDate;
9 | org.joda.time.MonthDay jodaMonthDay;
10 | org.joda.time.LocalTime jodaLocalTime;
11 | org.joda.time.LocalDateTime jodaLocalDateTime;
12 | org.joda.time.Instant jodaOffsetDateTime;
13 | }
14 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/MapClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | import java.util.Map;
7 | import java.util.concurrent.ConcurrentMap;
8 |
9 | @SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
10 | @Data
11 | public final class MapClass {
12 | private ConcurrentMap mapField;
13 | private Map enumKeyMapField;
14 | private CustomMap customMapField;
15 | private Map> nestedMapField;
16 | }
17 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/MathClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import lombok.Data;
4 | import online.sharedtype.SharedType;
5 |
6 | import java.math.BigDecimal;
7 | import java.math.BigInteger;
8 |
9 | @SharedType(
10 | rustMacroTraits = {"PartialEq", "Eq", "Hash", "serde::Serialize", "serde::Deserialize"}
11 | )
12 | @Data
13 | public final class MathClass {
14 | private BigInteger bigInteger;
15 | private BigDecimal bigDecimal;
16 | }
17 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/OptionalMethod.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import com.fasterxml.jackson.annotation.JsonGetter;
4 | import lombok.Setter;
5 | import online.sharedtype.SharedType;
6 |
7 | import java.util.Collections;
8 | import java.util.Map;
9 | import java.util.Optional;
10 | import java.util.Set;
11 |
12 | @SharedType(
13 | rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"},
14 | typescriptOptionalFieldFormat = {"undefined", "null"},
15 | typescriptFieldReadonlyType = "none"
16 | )
17 | @Setter
18 | public class OptionalMethod {
19 | @SharedType.Ignore
20 | private String value;
21 |
22 | @JsonGetter
23 | Optional getValueOptional() {
24 | return Optional.ofNullable(value);
25 | }
26 |
27 | @JsonGetter
28 | Optional> getNestedValueOptional() {
29 | return Optional.of(Optional.ofNullable(value));
30 | }
31 |
32 | @JsonGetter
33 | Optional>> getSetNestedValueOptional() {
34 | return Optional.of(Collections.singleton(Optional.ofNullable(value)));
35 | }
36 |
37 | @JsonGetter
38 | Optional>> getMapNestedValueOptional() {
39 | return Optional.of(Collections.singletonMap(1, Optional.ofNullable(value)));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/RecursiveClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | @SharedType(rustMacroTraits = {"PartialEq", "serde::Serialize", "serde::Deserialize"})
6 | final class RecursiveClass {
7 | RecursiveClass directRef;
8 | RecursiveClass[] arrayRef;
9 | }
10 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/SuperClassA.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import lombok.EqualsAndHashCode;
5 | import lombok.Getter;
6 | import lombok.Setter;
7 |
8 | @EqualsAndHashCode(callSuper = false)
9 | @Setter
10 | @Getter
11 | public abstract class SuperClassA extends IgnoredSuperClassB implements InterfaceSubA, IgnoredInterfaceB {
12 | private int a;
13 |
14 | @JsonProperty(access = JsonProperty.Access.READ_ONLY)
15 | @Override
16 | public Integer getValue() {
17 | return a;
18 | }
19 |
20 | @JsonProperty(access = JsonProperty.Access.READ_ONLY)
21 | @Override
22 | public int getNotIgnoredImplementedMethod() {
23 | return 0;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/TempClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | import java.math.BigDecimal;
6 |
7 | @SharedType(includes = SharedType.ComponentType.CONSTANTS)
8 | public class TempClass extends IgnoredSuperClassB {
9 | // static final BigDecimal VALUE2 = BigDecimal.valueOf(555);
10 | }
11 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/anno/AsAccessor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.anno;
2 |
3 | @interface AsAccessor {
4 | }
5 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/anno/AsEnumValue.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.anno;
2 |
3 | @interface AsEnumValue {
4 | }
5 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/anno/CustomAnnoClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.anno;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.SharedType;
5 |
6 | @SharedType
7 | final class CustomAnnoClass {
8 | @ToIgnore
9 | int ignoredField;
10 |
11 | @AsAccessor
12 | int someValue() {
13 | return 1;
14 | }
15 | }
16 |
17 | @RequiredArgsConstructor
18 | @SharedType
19 | enum CustomAnnoEnum {
20 | A(1), B(2)
21 | ;
22 | @AsEnumValue
23 | private final int value;
24 | }
25 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/anno/ToIgnore.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.anno;
2 |
3 | @interface ToIgnore {
4 | }
5 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/other/JavaClass.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.other;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | @SharedType(name = "AnotherJavaClass")
6 | public class JavaClass {
7 | int value;
8 | }
9 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/other/OtherConstants.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.other;
2 |
3 | public final class OtherConstants {
4 | public static final long LONG_VALUE = 666L;
5 |
6 | public static final class Inner1 {
7 | public static final long STATIC_IMPORTED_VALUE = 999L;
8 | public static final long INNER_LONG_VALUE_IN_STATIC_BLOCK;
9 | static {
10 | INNER_LONG_VALUE_IN_STATIC_BLOCK = 787L;
11 | }
12 |
13 | public static final class Inner2 {
14 | public static final long INNER_LONG_VALUE = 777L;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/it/java8/src/main/java/online/sharedtype/it/java8/types/ArbitraryTypeMapping.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.it.java8.types;
2 |
3 | import online.sharedtype.SharedType;
4 |
5 | @SharedType.Ignore
6 | class MyType1 {
7 | T value;
8 | }
9 |
10 | class MyType2 {
11 | int value;
12 | }
13 |
14 | // mapping defined in it/sharedtype.properties
15 | @SharedType
16 | class ArbitraryTypeMapping {
17 | private MyType1 myType1T;
18 | private MyType1 myType1String;
19 | private MyType2 myType2;
20 | }
21 |
22 | // todo: 1. SharedType.Ignore won't raise error.
23 | // 2. update mapped name at earlier stage into domain object to simplify mapping logics in type converters.
24 | // 3. verify global properties to ignore types
25 |
--------------------------------------------------------------------------------
/it/sharedtype.properties:
--------------------------------------------------------------------------------
1 | sharedtype.targets=CONSOLE, JAVA_SERIALIZED, TYPESCRIPT, RUST, GO
2 | sharedtype.ignore-annotations=online.sharedtype.it.java8.anno.ToIgnore
3 | sharedtype.accessor-annotations=online.sharedtype.it.java8.anno.AsAccessor
4 | sharedtype.enum-value-annotations=online.sharedtype.it.java8.anno.AsEnumValue
5 |
6 | sharedtype.typescript.constant-inlined=false
7 | sharedtype.typescript.type-mappings=online.sharedtype.it.java8.types.MyType1:Array,\
8 | online.sharedtype.it.java8.types.MyType2:number
9 |
10 | sharedtype.rust.type-mappings=online.sharedtype.it.java8.types.MyType1:Vec
11 |
12 | sharedtype.go.type-mappings=online.sharedtype.it.java8.types.MyType1:[]
13 |
--------------------------------------------------------------------------------
/misc/logo-color.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #set -e
3 |
4 | # To test locally, need to prepare GPG keys
5 | # gpg --gen-key
6 | # See also: https://maven.apache.org/plugins/maven-gpg-plugin/
7 | # See also: https://stackoverflow.com/questions/3174537/how-to-transfer-pgp-private-key-to-another-computer
8 |
9 | if [ -z "$MAVEN_GPG_PASSPHRASE" ];then
10 | echo "No MAVEN_GPG_PASSPHRASE provided, exit 1"
11 | exit 1
12 | fi
13 |
14 | # https://stackoverflow.com/a/64390598/5172925
15 | ### Increments the part of the string
16 | ## $1: version itself
17 | ## $2: number of part: 0 – major, 1 – minor, 2 – patch
18 | increment_version() {
19 | local delimiter='.'
20 | IFS=$delimiter read -r -a array <<< "$1"
21 | array[$2]=$((array[$2]+1))
22 | if [ $2 -lt 2 ]; then array[2]=0; fi
23 | if [ $2 -lt 1 ]; then array[1]=0; fi
24 | printf '%s' "$(local IFS=$delimiter ; echo "${array[*]}")"
25 | }
26 |
27 | snapshotVersion=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
28 | version="$(printf '%s' "$snapshotVersion" | sed -e "s/-SNAPSHOT//g")"
29 |
30 | ./mvnw versions:set -DgenerateBackupPoms=false -DnewVersion="$version" --ntp -B
31 | sed -i -E "s/[0-9]+\.[0-9\.\[0-9]+\.[0-9]+<\/sharedtype\.version>/$version<\/sharedtype.version>/g" doc/Usage.md
32 | ./mvnw deploy -DskipTests -Prelease --ntp -B # to debug release can add -DskipPublishing=true to prevent actual upload
33 | NEW_VERSION="$(increment_version "$version" 1)-SNAPSHOT"
34 | ./mvnw versions:set -DgenerateBackupPoms=false -DnewVersion="$NEW_VERSION" --ntp -B
35 | printf '%s' "$NEW_VERSION" > NEW_VERSION.cache
36 |
--------------------------------------------------------------------------------
/misc/setversion.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ./mvnw versions:set -DgenerateBackupPoms=false -DnewVersion="$1" --ntp
4 |
--------------------------------------------------------------------------------
/misc/start-client-servers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # this script is used by JsonE2eTest to spin up client servers
3 |
4 |
5 |
6 | BASE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
7 | echo "Base dir: $BASE_DIR"
8 |
9 | cd "$BASE_DIR/client-test/typescript" || exit 1
10 | . setenv
11 | npm i && npm run start &
12 |
13 | cd "$BASE_DIR/client-test/go" || exit 1
14 | . setenv
15 | go run server.go &
16 |
17 | cd "$BASE_DIR/client-test/rust" || exit 1
18 | cargo run &
19 |
20 | # kill all child processes upon exit
21 | trap 'pkill -P $$' EXIT
22 |
23 | # wait for signal to exit
24 | wait
25 |
--------------------------------------------------------------------------------
/mount-tmpfs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
5 | MAVEN_REPO_CACHE_PATH="$DIR/.run/maven-repository-path.cache"
6 | if [ -f "$MAVEN_REPO_CACHE_PATH" ]; then
7 | MAVEN_REPO_DIR=$(cat "$MAVEN_REPO_CACHE_PATH")
8 | else
9 | MAVEN_REPO_DIR="$(./mvnw help:evaluate -Dexpression=settings.localRepository -q -DforceStdout)/online/sharedtype"
10 | printf '%s' "$MAVEN_REPO_DIR" > "$MAVEN_REPO_CACHE_PATH"
11 | fi
12 |
13 | function mountTmpfs() {
14 | mkdir -p "$1"
15 | sudo mount -t tmpfs -o size="$2" -o noatime tmpfs "$1"
16 | echo "tmpfs mounted at $1 of size $2"
17 | }
18 |
19 | mountTmpfs "$DIR/target" 8M
20 | mountTmpfs "$DIR/annotation/target" 32M
21 | mountTmpfs "$DIR/processor/target" 64M
22 | mountTmpfs "$DIR/it/java17/target" 64M
23 | mountTmpfs "$DIR/it/java8/target" 64M
24 | mountTmpfs "$DIR/e2e/target" 64M
25 | mountTmpfs "$MAVEN_REPO_DIR" 64M
26 | mountTmpfs "$DIR/client-test/rust/target" 512M
27 | mountTmpfs "$DIR/client-test/typescript/dist" 32M
28 |
--------------------------------------------------------------------------------
/mvne:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export MAVEN_OPTS="-Xdebug -Xnoagent -Xint -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 $MAVEN_OPTS"
4 | ./mvnw "$@"
5 |
--------------------------------------------------------------------------------
/processor/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module online.sharedtype.processor {
2 | requires online.sharedtype.annotation;
3 | requires java.base;
4 | requires java.compiler;
5 | requires jdk.compiler;
6 | requires static lombok;
7 | requires static com.google.auto.service;
8 |
9 | requires com.github.mustachejava;
10 |
11 | provides javax.annotation.processing.Processor with online.sharedtype.processor.AnnotationProcessorImpl;
12 | }
13 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/context/EnumCtorIndex.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.Getter;
4 | import lombok.RequiredArgsConstructor;
5 |
6 | import online.sharedtype.processor.support.annotation.Nullable;
7 | import javax.lang.model.element.VariableElement;
8 |
9 |
10 | @RequiredArgsConstructor
11 | @Getter
12 | public final class EnumCtorIndex {
13 | public static final EnumCtorIndex NONE = new EnumCtorIndex(-1, null);
14 | /**
15 | * The index of enum constant constructor argument, or -1 if no constructor argument.
16 | */
17 | private final int idx;
18 | private final VariableElement field;
19 | }
20 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/context/EnumParsingUtils.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.experimental.UtilityClass;
4 |
5 | import java.util.Collection;
6 | import java.util.EnumSet;
7 | import java.util.Set;
8 | import java.util.function.Function;
9 |
10 | @UtilityClass
11 | final class EnumParsingUtils {
12 | /** Match by provided function */
13 | static > Set parseEnumSet(Collection values, Class type, Function enumValueOf) {
14 | Set set = EnumSet.noneOf(type);
15 | for (String trimmed : values) {
16 | set.add(enumValueOf.apply(trimmed));
17 | }
18 | return set;
19 | }
20 |
21 | /** Match uppercase of values */
22 | static > Set parseEnumSet(Collection values, Class type) {
23 | Set set = EnumSet.noneOf(type);
24 | for (String trimmed : values) {
25 | set.add(Enum.valueOf(type, trimmed.toUpperCase()));
26 | }
27 | return set;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/context/OutputTarget.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.Getter;
4 | import lombok.RequiredArgsConstructor;
5 | import online.sharedtype.SharedType;
6 |
7 | import online.sharedtype.processor.support.annotation.Nullable;
8 |
9 | /**
10 | * @author Cause Chung
11 | */
12 | @Getter
13 | @RequiredArgsConstructor
14 | public enum OutputTarget {
15 | /** Print metadata to console. */
16 | CONSOLE,
17 | /** Write metadata to Java serialized file. Used for integration test. */
18 | JAVA_SERIALIZED,
19 | TYPESCRIPT(SharedType.TargetType.TYPESCRIPT),
20 | GO(SharedType.TargetType.GO),
21 | RUST(SharedType.TargetType.RUST),
22 | ;
23 | @Nullable
24 | private final SharedType.TargetType targetType;
25 |
26 | OutputTarget() {
27 | this(null);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/context/RenderFlags.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 |
6 | /**
7 | * Flags that can only be marked during processing runtime.
8 | * Anything that can be configured beforehand should be in {@link Props}.
9 | */
10 | @Getter
11 | @Setter
12 | public final class RenderFlags {
13 | private boolean useRustAny = false;
14 | private boolean useRustMap = false;
15 | }
16 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/CompositeTypeDefParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.component.AbstractComponentInfo;
6 | import online.sharedtype.processor.domain.component.ComponentInfo;
7 | import online.sharedtype.processor.domain.def.TypeDef;
8 |
9 | import javax.lang.model.element.TypeElement;
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | /**
15 | * @author Cause Chung
16 | */
17 | @RequiredArgsConstructor
18 | final class CompositeTypeDefParser implements TypeDefParser {
19 | private final Context ctx;
20 | private final List parsers;
21 |
22 | @Override
23 | public List parse(TypeElement typeElement) {
24 | if (ctx.isIgnored(typeElement)) {
25 | return Collections.emptyList();
26 | }
27 | String qualifiedName = typeElement.getQualifiedName().toString();
28 | List cachedDef = ctx.getTypeStore().getTypeDefs(qualifiedName);
29 | if (cachedDef != null) {
30 | return new ArrayList<>(cachedDef);
31 | }
32 |
33 | if (ctx.isArraylike(typeElement.asType())) {
34 | ctx.warn(typeElement, "Type '%s' is an array type, which cannot be parsed and emitted as a standalone type.", typeElement.getQualifiedName());
35 | return Collections.emptyList();
36 | }
37 | if (ctx.isDatetimelike(typeElement.asType())) {
38 | ctx.warn(typeElement, "Type '%s' is a datetime type, which cannot be parsed and emitted as a standalone type.", typeElement.getQualifiedName());
39 | return Collections.emptyList();
40 | }
41 | // TODO: warn for maplikeType
42 |
43 | ctx.info("Processing: %s", typeElement.getQualifiedName());
44 | List typeDefs = new ArrayList<>();
45 | for (TypeDefParser typeDefParser : parsers) {
46 | List parsedTypeDefs = typeDefParser.parse(typeElement);
47 | for (TypeDef parsedTypeDef : parsedTypeDefs) {
48 | ctx.getTypeStore().saveTypeDef(qualifiedName, parsedTypeDef);
49 | populateTagLiterals(parsedTypeDef);
50 | }
51 | typeDefs.addAll(parsedTypeDefs);
52 | }
53 | return typeDefs;
54 | }
55 |
56 | private void populateTagLiterals(TypeDef typeDef) {
57 | for (ComponentInfo component : typeDef.components()) {
58 | if (component instanceof AbstractComponentInfo) {
59 | AbstractComponentInfo abstractComponentInfo = (AbstractComponentInfo) component;
60 | abstractComponentInfo.setTagLiterals(ctx.extractTagLiterals(abstractComponentInfo.getElement()));
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/TypeDefParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.parser.type.TypeInfoParser;
6 | import online.sharedtype.processor.parser.value.ValueParser;
7 |
8 | import javax.lang.model.element.TypeElement;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * Parse type structural information.
14 | *
15 | * @see TypeDef
16 | * @see TypeInfoParser
17 | * @author Cause Chung
18 | */
19 | public interface TypeDefParser {
20 | /**
21 | * Parse structural information.
22 | *
23 | * @return empty if the typeElement is ignored or invalid.
24 | * A typeElement can be ignored via configuration.
25 | * An invalid type can be an unsupported type, e.g. a non-static inner class.
26 | * The main classDef or enumDef must be the first element in the list, constantDef should be the 2nd element if exists.
27 | */
28 | List parse(TypeElement typeElement);
29 |
30 | static TypeDefParser create(Context ctx) {
31 | TypeInfoParser typeInfoParser = TypeInfoParser.create(ctx);
32 | ValueParser valueParser = ValueParser.create(ctx, typeInfoParser);
33 | List parsers = new ArrayList<>(3); // order matters! see #parse
34 | parsers.add(new ClassTypeDefParser(ctx, typeInfoParser));
35 | parsers.add(new EnumTypeDefParser(ctx, typeInfoParser, valueParser));
36 | parsers.add(new ConstantTypeDefParser(ctx, typeInfoParser, valueParser));
37 | return new CompositeTypeDefParser(ctx, parsers);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/type/MappableTypeInfoParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.type;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.MappableType;
6 | import online.sharedtype.processor.domain.type.TypeInfo;
7 |
8 | import javax.lang.model.element.TypeElement;
9 | import javax.lang.model.type.TypeMirror;
10 | import java.util.Map;
11 |
12 | final class MappableTypeInfoParser implements TypeInfoParser {
13 | private final TypeInfoParser delegate;
14 | private final Map typescriptTypeMappings;
15 | private final Map goTypeMappings;
16 | private final Map rustTypeMappings;
17 |
18 | MappableTypeInfoParser(Context ctx, TypeInfoParser delegate) {
19 | this.delegate = delegate;
20 | this.typescriptTypeMappings = ctx.getProps().getTypescript().getTypeMappings();
21 | this.goTypeMappings = ctx.getProps().getGo().getTypeMappings();
22 | this.rustTypeMappings = ctx.getProps().getRust().getTypeMappings();
23 | }
24 |
25 | @Override
26 | public TypeInfo parse(TypeMirror typeMirror, TypeElement ctxTypeElement) {
27 | TypeInfo typeInfo = delegate.parse(typeMirror, ctxTypeElement);
28 |
29 | if (typeInfo instanceof MappableType) {
30 | MappableType mappableType = (MappableType) typeInfo;
31 | updateTypeMappings(mappableType, SharedType.TargetType.TYPESCRIPT, typescriptTypeMappings);
32 | updateTypeMappings(mappableType, SharedType.TargetType.GO, goTypeMappings);
33 | updateTypeMappings(mappableType, SharedType.TargetType.RUST, rustTypeMappings);
34 | }
35 | return typeInfo;
36 | }
37 |
38 | private static void updateTypeMappings(MappableType mappableType, SharedType.TargetType targetType, Map typeMappings) {
39 | String mappedName = typeMappings.get(mappableType.qualifiedName());
40 | if (mappedName != null) {
41 | mappableType.addMappedName(targetType, mappedName);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/type/TypeInfoParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.type;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.parser.TypeDefParser;
5 | import online.sharedtype.processor.domain.type.TypeInfo;
6 | import online.sharedtype.processor.context.Context;
7 |
8 | import javax.lang.model.element.TypeElement;
9 | import javax.lang.model.type.TypeMirror;
10 |
11 | /**
12 | * Parse type specific information.
13 | *
14 | * @see TypeInfo
15 | * @see TypeDefParser
16 | * @author Cause Chung
17 | */
18 | public interface TypeInfoParser {
19 | /**
20 | * Parse type specific information.
21 | *
22 | * If a dependent type is not explicitly registered by {@link SharedType},
23 | * it may not have been resolved in the context by the time of the call.
24 | * In such case, the caller would resolve the type in another iteration and resolve the type again.
25 | *
26 | *
27 | * An unresolved type is a type whose TypeElement hasn't been processed.
28 | * So there's no structural information, e.g. name, saved in global context yet.
29 | *
30 | *
31 | * @return a no type info instance if the kind of the typeMirror is ERROR.
32 | */
33 | TypeInfo parse(TypeMirror typeMirror, TypeElement ctxTypeElement);
34 |
35 | static TypeInfoParser create(Context ctx) {
36 | return new MappableTypeInfoParser(ctx, new TypeInfoParserImpl(ctx));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/value/CompositeValueParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.value;
2 |
3 | import online.sharedtype.processor.domain.value.ValueHolder;
4 | import online.sharedtype.processor.support.exception.SharedTypeInternalError;
5 |
6 | import javax.lang.model.element.Element;
7 | import javax.lang.model.element.ElementKind;
8 | import javax.lang.model.element.TypeElement;
9 | import java.util.EnumMap;
10 | import java.util.Map;
11 |
12 | final class CompositeValueParser implements ValueParser {
13 | private final Map resolvers = new EnumMap<>(ElementKind.class);
14 |
15 | void registerResolver(ElementKind kind, ValueParser resolver) {
16 | resolvers.put(kind, resolver);
17 | }
18 |
19 | @Override
20 | public ValueHolder resolve(Element element, TypeElement ctxTypeElement) {
21 | ValueParser resolver = resolvers.get(element.getKind());
22 | if (resolver != null) {
23 | return resolver.resolve(element, ctxTypeElement);
24 | }
25 | throw new SharedTypeInternalError(String.format("Unable to resolve value for element '%s', unsupported element kind: %s", element, element.getKind()));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/value/ConstantValueParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.value;
2 |
3 | import com.sun.source.tree.Tree;
4 | import lombok.RequiredArgsConstructor;
5 | import online.sharedtype.processor.context.Context;
6 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
7 | import online.sharedtype.processor.domain.type.TypeInfo;
8 | import online.sharedtype.processor.domain.value.ValueHolder;
9 | import online.sharedtype.processor.parser.type.TypeInfoParser;
10 | import online.sharedtype.processor.support.exception.SharedTypeException;
11 |
12 | import javax.lang.model.element.Element;
13 | import javax.lang.model.element.TypeElement;
14 |
15 | @RequiredArgsConstructor
16 | final class ConstantValueParser implements ValueParser {
17 | private final Context ctx;
18 | private final TypeInfoParser typeInfoParser;
19 | private final ValueResolverBackend valueResolverBackend;
20 |
21 | @Override
22 | public ValueHolder resolve(Element fieldElement, TypeElement ctxTypeElement) {
23 | Tree tree = ctx.getTrees().getTree(fieldElement);
24 | if (tree == null) {
25 | ctx.error(fieldElement, "Cannot parse constant value for field: %s in %s, tree is null from the field element. " +
26 | "If the type is from a dependency jar/compiled class file, tree is not available at the time of annotation processing. " +
27 | "Check if the type or its custom mapping is correct.",
28 | fieldElement, ctxTypeElement);
29 | return ValueHolder.NULL;
30 | }
31 | try {
32 | ValueResolveContext parsingContext = ValueResolveContext.builder(ctx)
33 | .fieldElement(fieldElement)
34 | .tree(tree).enclosingTypeElement(ctxTypeElement)
35 | .build();
36 | TypeInfo fieldTypeInfo = typeInfoParser.parse(fieldElement.asType(), ctxTypeElement);
37 | if (fieldTypeInfo instanceof ConcreteTypeInfo) {
38 | ConcreteTypeInfo valueType = (ConcreteTypeInfo) fieldTypeInfo;
39 | return ValueHolder.of(valueType, valueResolverBackend.recursivelyResolve(parsingContext));
40 | }
41 | ctx.error(fieldElement, "Complex field types are not supported for value resolving. Only literal types or references are supported," +
42 | " the type is '%s'.", fieldElement.asType());
43 | return ValueHolder.NULL;
44 | } catch (SharedTypeException e) {
45 | ctx.error(fieldElement, "Failed to parse constant value. " +
46 | "Field tree: '%s' in '%s'. Consider to ignore this field or exclude constants generation for this type. " +
47 | "Error message: %s",
48 | tree, ctxTypeElement, e.getMessage());
49 | }
50 | return ValueHolder.NULL;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/value/ValueParser.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.value;
2 |
3 | import online.sharedtype.processor.context.Context;
4 | import online.sharedtype.processor.domain.value.ValueHolder;
5 | import online.sharedtype.processor.parser.type.TypeInfoParser;
6 |
7 | import javax.lang.model.element.Element;
8 | import javax.lang.model.element.ElementKind;
9 | import javax.lang.model.element.TypeElement;
10 |
11 | public interface ValueParser {
12 | ValueHolder resolve(Element element, TypeElement ctxTypeElement);
13 |
14 | static ValueParser create(Context ctx, TypeInfoParser typeInfoParser) {
15 | CompositeValueParser compositeValueResolver = new CompositeValueParser();
16 | ValueResolverBackend backend = new ValueResolverBackendImpl(compositeValueResolver);
17 | compositeValueResolver.registerResolver(ElementKind.ENUM_CONSTANT, new EnumValueParser(ctx, typeInfoParser, backend));
18 | compositeValueResolver.registerResolver(ElementKind.FIELD, new ConstantValueParser(ctx, typeInfoParser, backend));
19 | return compositeValueResolver;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/parser/value/ValueResolverBackend.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.value;
2 |
3 | interface ValueResolverBackend {
4 | Object recursivelyResolve(ValueResolveContext parsingContext);
5 | }
6 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/resolver/CompositeTypeResolver.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | final class CompositeTypeResolver implements TypeResolver {
9 | private final List resolvers;
10 |
11 | public CompositeTypeResolver(TypeResolver... resolvers) {
12 | this.resolvers = Arrays.asList(resolvers);
13 | }
14 |
15 | @Override
16 | public List resolve(List typeDefs) {
17 | List result = typeDefs;
18 | for (TypeResolver resolver : resolvers) {
19 | result = resolver.resolve(result);
20 | }
21 | return result;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/resolver/ReferenceResolver.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.processor.domain.def.ConcreteTypeDef;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 | import online.sharedtype.processor.support.annotation.SideEffect;
6 |
7 | import java.util.ArrayDeque;
8 | import java.util.Deque;
9 | import java.util.HashSet;
10 | import java.util.List;
11 | import java.util.Set;
12 | import java.util.stream.Collectors;
13 |
14 | /**
15 | * Traverse type graph to mark:
16 | *
17 | * cyclic references
18 | * direct or indirect reference from explicitly annotated classes
19 | *
20 | */
21 | final class ReferenceResolver implements TypeResolver {
22 |
23 | @Override
24 | public List resolve(@SideEffect List typeDefs) {
25 | for (TypeDef typeDef : typeDefs) {
26 | traverse(typeDef);
27 | }
28 | return typeDefs;
29 | }
30 |
31 | private static void traverse(TypeDef typeDef) {
32 | Set visited = new HashSet<>();
33 | Deque typeDefStack = new ArrayDeque<>();
34 | typeDefStack.push(typeDef);
35 | boolean referencedByAnnotated = typeDef.isReferencedByAnnotated();
36 |
37 | while (!typeDefStack.isEmpty()) {
38 | TypeDef cur = typeDefStack.pop();
39 | if (visited.contains(cur)) {
40 | cur.setCyclicReferenced(true);
41 | continue;
42 | } else {
43 | visited.add(cur);
44 | }
45 |
46 | if (cur instanceof ConcreteTypeDef) {
47 | ConcreteTypeDef curConcreteTypeDef = (ConcreteTypeDef) cur;
48 | List referencingTypeDefs = curConcreteTypeDef.typeInfoSet().stream()
49 | .flatMap(ts -> ts.referencingTypes().stream()).collect(Collectors.toList());
50 | if (!referencingTypeDefs.isEmpty()){
51 | curConcreteTypeDef.setDepended(true);
52 | }
53 | for (TypeDef referencingTypeDef : referencingTypeDefs) {
54 | typeDefStack.push(referencingTypeDef);
55 | if (referencingTypeDef instanceof ConcreteTypeDef) {
56 | ConcreteTypeDef dependingClassDef = (ConcreteTypeDef) referencingTypeDef;
57 | if (dependingClassDef.isAnnotated() || dependingClassDef.isReferencedByAnnotated()) {
58 | cur.setReferencedByAnnotated(true);
59 | referencedByAnnotated = true;
60 | }
61 | }
62 | }
63 | }
64 | }
65 | typeDef.setReferencedByAnnotated(referencedByAnnotated);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/resolver/SubtypeResolver.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 | import online.sharedtype.processor.domain.def.TypeDef;
6 | import online.sharedtype.processor.domain.type.TypeInfo;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Resolve subtype relationships.
12 | */
13 | final class SubtypeResolver implements TypeResolver {
14 | @Override
15 | public List resolve(List typeDefs) {
16 | for (TypeDef typeDef : typeDefs) {
17 | traverseDirectSupertypes(typeDef);
18 | }
19 | return typeDefs;
20 | }
21 |
22 | private static void traverseDirectSupertypes(TypeDef typeDef) {
23 | for (TypeInfo typeInfo : typeDef.directSupertypes()) {
24 | if (typeInfo instanceof ConcreteTypeInfo) {
25 | ConcreteTypeInfo concreteTypeInfo = (ConcreteTypeInfo) typeInfo;
26 | TypeDef supertypeDef = concreteTypeInfo.typeDef();
27 | if (supertypeDef instanceof ClassDef) {
28 | ClassDef supertypeClassDef = (ClassDef) supertypeDef;
29 | supertypeClassDef.addSubtype(typeDef);
30 | supertypeClassDef.setDepended(true);
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/resolver/TypeResolver.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 | import online.sharedtype.processor.domain.type.TypeInfo;
6 | import online.sharedtype.processor.context.Context;
7 | import online.sharedtype.processor.parser.TypeDefParser;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Resolve required but unknown type information after initial parsing stage.
13 | *
14 | * @see TypeDefParser
15 | * @author Cause Chung
16 | */
17 | public interface TypeResolver {
18 | /**
19 | * Resolve {@link TypeInfo} by traversing all types.
20 | *
21 | * @param typeDefs the types discovered in initial parsing stage, they are types directly annotated with {@link SharedType}.
22 | * @return all type definitions needed to generate output. Including dependency types, e.g. referenced types, super types.
23 | */
24 | List resolve(List typeDefs);
25 |
26 | static TypeResolver create(Context ctx, TypeDefParser typeDefParser) {
27 | // order matters
28 | // TypeInfo and TypeDef resolution is completed in LoopTypeResolver,
29 | // Other resolvers may depend on the result.
30 | return new CompositeTypeResolver(
31 | new LoopTypeResolver(ctx, typeDefParser),
32 | new OptionalTypeResolver(ctx),
33 | new ReferenceResolver(),
34 | new SubtypeResolver()
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/Preconditions.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support;
2 |
3 | import online.sharedtype.processor.support.exception.SharedTypeInternalError;
4 |
5 | import java.util.Collection;
6 |
7 | /**
8 | *
9 | * @author Cause Chung
10 | */
11 | // TODO: remove varargs to improve performance
12 | public final class Preconditions {
13 | private Preconditions() {
14 | }
15 |
16 |
17 | public static void checkArgument(boolean condition, String message, Object... objects) {
18 | if (!condition) {
19 | throw new SharedTypeInternalError(String.format(message, objects));
20 | }
21 | }
22 |
23 | public static T requireNonNull(T o, String message, Object... objects) {
24 | if (o == null) {
25 | throw new SharedTypeInternalError(String.format(message, objects));
26 | }
27 | return o;
28 | }
29 |
30 | public static > T requireNonEmpty(T c, String message, Object... objects) {
31 | if (c.isEmpty()) {
32 | throw new SharedTypeInternalError(String.format(message, objects));
33 | }
34 | return c;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/exception/SharedTypeException.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.exception;
2 |
3 | /**
4 | * Indicate an exception, equivalent to {@link RuntimeException}.
5 | * Note: compilation error should be printed via {@link online.sharedtype.processor.context.Context#error}.
6 | *
7 | * @author Cause Chung
8 | */
9 | public final class SharedTypeException extends RuntimeException {
10 | public SharedTypeException(String message) {
11 | super(message);
12 | }
13 |
14 | public SharedTypeException(String message, Throwable cause) {
15 | super(message, cause);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/exception/SharedTypeInternalError.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.exception;
2 |
3 | import online.sharedtype.processor.support.github.RepositoryInfo;
4 |
5 | /**
6 | * Indicate an error.
7 | *
8 | * @author Cause Chung
9 | */
10 | public final class SharedTypeInternalError extends Error {
11 | public SharedTypeInternalError(String message) {
12 | super(format(message));
13 | }
14 |
15 | public SharedTypeInternalError(String message, Throwable cause) {
16 | super(format(message), cause);
17 | }
18 |
19 | private static String format(String message) {
20 | return String.format("%s (this could be an implementation error, please post an issue at %s/issues)", message, RepositoryInfo.PROJECT_REPO_URL);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/github/RepositoryInfo.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.github;
2 |
3 | /**
4 | * @author Cause Chung
5 | */
6 | public final class RepositoryInfo {
7 | public static final String PROJECT_REPO_URL = "https://github.com/cuzfrog/sharedtype";
8 | }
9 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/utils/Tuple.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.utils;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.RequiredArgsConstructor;
5 |
6 | /**
7 | * @author Cause Chung
8 | */
9 | @EqualsAndHashCode
10 | @RequiredArgsConstructor
11 | public final class Tuple {
12 | private final A a;
13 | private final B b;
14 |
15 | public static Tuple of(A a, B b) {
16 | return new Tuple<>(a, b);
17 | }
18 |
19 | public A a() {
20 | return a;
21 | }
22 |
23 | public B b() {
24 | return b;
25 | }
26 |
27 | @Override
28 | public String toString() {
29 | return String.format("(%s, %s)", a, b);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/support/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.support.utils;
2 |
3 | import lombok.experimental.UtilityClass;
4 | import online.sharedtype.SharedType;
5 | import online.sharedtype.processor.support.exception.SharedTypeException;
6 | import online.sharedtype.processor.support.exception.SharedTypeInternalError;
7 |
8 | import javax.lang.model.element.Element;
9 | import javax.lang.model.element.ElementKind;
10 | import javax.lang.model.element.Modifier;
11 | import javax.lang.model.element.TypeElement;
12 | import javax.lang.model.element.VariableElement;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.util.function.Supplier;
16 |
17 | /**
18 | * @author Cause Chung
19 | */
20 | @UtilityClass
21 | public final class Utils {
22 | private static final String[] EMPTY_STRING_ARRAY = new String[0];
23 |
24 | public static String substringAndUncapitalize(String str, int beginIndex) {
25 | try{
26 | return Character.toLowerCase(str.charAt(beginIndex)) + str.substring(beginIndex + 1); // TODO: see if can optimize
27 | } catch (IndexOutOfBoundsException e) {
28 | throw new SharedTypeInternalError(String.format("Failed to substringAndUncapitalize string: '%s'", str) ,e);
29 | }
30 | }
31 |
32 | public static String[] emptyStringArray() {
33 | return EMPTY_STRING_ARRAY;
34 | }
35 |
36 | public static String notEmptyOrDefault(String value, String defaultValue, Supplier message) {
37 | String res = value != null && !value.isEmpty() ? value : defaultValue;
38 | if (res == null || res.isEmpty()) {
39 | throw new SharedTypeException("Either value or defaultValue must not be empty. " + message.get());
40 | }
41 | return res;
42 | }
43 |
44 | public static List allInstanceFields(TypeElement typeElement) {
45 | List extends Element> enclosedElements = typeElement.getEnclosedElements();
46 | List fields = new ArrayList<>(enclosedElements.size());
47 | for (Element enclosedElement : enclosedElements) {
48 | if (enclosedElement.getKind() == ElementKind.FIELD && !enclosedElement.getModifiers().contains(Modifier.STATIC)) {
49 | fields.add((VariableElement) enclosedElement);
50 | }
51 | }
52 | return fields;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/CompositeWriter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 |
6 | import java.io.IOException;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | /**
11 | *
12 | * @author Cause Chung
13 | */
14 | @RequiredArgsConstructor
15 | final class CompositeWriter implements TypeWriter{
16 | private final Set writers;
17 |
18 | @Override
19 | public void write(List typeDefs) throws IOException {
20 | for (TypeWriter writer : writers) {
21 | writer.write(typeDefs);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/ConsoleWriter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 | import online.sharedtype.processor.context.Context;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | *
11 | * @author Cause Chung
12 | */
13 | @RequiredArgsConstructor
14 | final class ConsoleWriter implements TypeWriter{
15 | private final Context ctx;
16 |
17 | @Override
18 | public void write(List typeDefs) {
19 | typeDefs.forEach(d-> ctx.info("Write type: %s%s", System.lineSeparator(), d));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/JavaSerializationFileWriter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer;
2 |
3 | import online.sharedtype.processor.domain.def.ConstantNamespaceDef;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 | import online.sharedtype.processor.context.Context;
6 | import online.sharedtype.processor.support.exception.SharedTypeException;
7 |
8 | import javax.annotation.processing.Filer;
9 | import javax.tools.FileObject;
10 | import javax.tools.StandardLocation;
11 | import java.io.IOException;
12 | import java.io.ObjectOutputStream;
13 | import java.io.OutputStream;
14 | import java.util.List;
15 |
16 | /**
17 | * For internal usage, where integration tests deserialize the generated files back to objects.
18 | *
19 | * @author Cause Chung
20 | */
21 | final class JavaSerializationFileWriter implements TypeWriter {
22 | private final Filer filer;
23 |
24 | JavaSerializationFileWriter(Context ctx) {
25 | this.filer = ctx.getProcessingEnv().getFiler();
26 | }
27 |
28 | @Override
29 | public void write(List typeDefs) {
30 | try {
31 | for (TypeDef typeDef : typeDefs) {
32 | FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", getTypeName(typeDef) + ".ser");
33 | try(OutputStream outputStream = file.openOutputStream();
34 | ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {
35 | oos.writeObject(typeDef);
36 | }
37 | }
38 | } catch (IOException e) {
39 | throw new SharedTypeException("Failed to write to file,", e);
40 | }
41 | }
42 |
43 | private String getTypeName(TypeDef typeDef) {
44 | if (typeDef instanceof ConstantNamespaceDef) {
45 | return String.format("$%s", typeDef.qualifiedName());
46 | }
47 | return typeDef.qualifiedName();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/TemplateTypeFileWriter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.def.ConstantNamespaceDef;
6 | import online.sharedtype.processor.domain.def.TypeDef;
7 | import online.sharedtype.processor.writer.adaptor.RenderDataAdaptorFactory;
8 | import online.sharedtype.processor.writer.converter.TemplateDataConverter;
9 | import online.sharedtype.processor.writer.render.Template;
10 | import online.sharedtype.processor.writer.render.TemplateRenderer;
11 | import online.sharedtype.processor.support.utils.Tuple;
12 |
13 | import javax.tools.FileObject;
14 | import java.io.IOException;
15 | import java.io.OutputStream;
16 | import java.io.OutputStreamWriter;
17 | import java.io.Writer;
18 | import java.util.ArrayList;
19 | import java.util.HashMap;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.Set;
23 |
24 | /**
25 | *
26 | * @author Cause Chung
27 | */
28 | @RequiredArgsConstructor
29 | final class TemplateTypeFileWriter implements TypeWriter {
30 | private final Context ctx;
31 | private final TemplateRenderer renderer;
32 | private final RenderDataAdaptorFactory renderDataAdaptorFactory;
33 | private final Set converters;
34 | private final String outputFileName;
35 |
36 | @Override
37 | public void write(List typeDefs) throws IOException {
38 | List> data = new ArrayList<>(typeDefs.size() * converters.size());
39 | data.add(renderDataAdaptorFactory.header(ctx));
40 |
41 | Map simpleNames = new HashMap<>(typeDefs.size());
42 | for (TypeDef typeDef : typeDefs) {
43 | TypeDef duplicate = typeDef instanceof ConstantNamespaceDef ? null : simpleNames.get(typeDef.simpleName()); // todo: split class/enum and constant duplication checks
44 | if (duplicate != null) {
45 | ctx.warn("Duplicate names found: %s and %s, which is not allowed in output code." +
46 | " You may use @SharedType(name=\"...\") to rename a type.", typeDef.qualifiedName(), duplicate.qualifiedName());
47 | }
48 | simpleNames.put(typeDef.simpleName(), typeDef);
49 | for (TemplateDataConverter converter : converters) {
50 | if (converter.shouldAccept(typeDef)) {
51 | data.add(converter.convert(typeDef));
52 | }
53 | }
54 | }
55 |
56 | FileObject file = ctx.createSourceOutput(outputFileName);
57 | try (OutputStream outputStream = file.openOutputStream();
58 | Writer writer = new OutputStreamWriter(outputStream)) {
59 | renderer.render(writer, data);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/TypeWriter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.context.OutputTarget;
6 | import online.sharedtype.processor.writer.adaptor.RenderDataAdaptorFactory;
7 | import online.sharedtype.processor.writer.converter.TemplateDataConverter;
8 | import online.sharedtype.processor.writer.render.TemplateRenderer;
9 |
10 | import java.io.IOException;
11 | import java.util.HashSet;
12 | import java.util.List;
13 | import java.util.Set;
14 |
15 | /**
16 | * Writes type meta to target output.
17 | *
18 | * @author Cause Chung
19 | */
20 | public interface TypeWriter {
21 | /**
22 | * Writes type meta to target output.
23 | *
24 | * @param typeDefs type definitions required to generate output, assumed to be completed.
25 | * @throws IOException if underlying IO error occurs
26 | */
27 | void write(List typeDefs) throws IOException;
28 |
29 | static TypeWriter create(Context ctx) {
30 | Set writers = new HashSet<>(OutputTarget.values().length);
31 | if (ctx.getProps().getTargets().contains(OutputTarget.CONSOLE)) {
32 | writers.add(new ConsoleWriter(ctx));
33 | }
34 | if (ctx.getProps().getTargets().contains(OutputTarget.JAVA_SERIALIZED)) {
35 | writers.add(new JavaSerializationFileWriter(ctx));
36 | }
37 |
38 | TemplateRenderer renderer = TemplateRenderer.create();
39 | if (ctx.getProps().getTargets().contains(OutputTarget.TYPESCRIPT)) {
40 | writers.add(new TemplateTypeFileWriter(
41 | ctx, renderer, RenderDataAdaptorFactory::typescript, TemplateDataConverter.typescript(ctx), ctx.getProps().getTypescript().getOutputFileName()
42 | ));
43 | }
44 | if (ctx.getProps().getTargets().contains(OutputTarget.GO)) {
45 | writers.add(new TemplateTypeFileWriter(
46 | ctx, renderer, RenderDataAdaptorFactory::go, TemplateDataConverter.go(ctx), ctx.getProps().getGo().getOutputFileName()
47 | ));
48 | }
49 | if (ctx.getProps().getTargets().contains(OutputTarget.RUST)) {
50 | writers.add(new TemplateTypeFileWriter(
51 | ctx, renderer, RenderDataAdaptorFactory::rust, TemplateDataConverter.rust(ctx), ctx.getProps().getRust().getOutputFileName()
52 | ));
53 | }
54 | return new CompositeWriter(writers);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/AbstractDataAdaptor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.context.RenderFlags;
6 | import online.sharedtype.processor.support.exception.SharedTypeException;
7 |
8 | import java.io.IOException;
9 | import java.nio.file.Files;
10 | import java.nio.file.Path;
11 | import java.nio.file.Paths;
12 |
13 | @RequiredArgsConstructor
14 | abstract class AbstractDataAdaptor implements RenderDataAdaptor {
15 | final Context ctx;
16 |
17 | @SuppressWarnings("unused")
18 | final RenderFlags renderFlags() {
19 | return ctx.getRenderFlags();
20 | }
21 |
22 | abstract String customCodeSnippet();
23 |
24 | static String readCustomCodeSnippet(String path) {
25 | Path customCodePath = Paths.get(path);
26 | if (Files.notExists(customCodePath)) {
27 | return "";
28 | }
29 |
30 | try {
31 | return new String(Files.readAllBytes(customCodePath));
32 | } catch (IOException e) {
33 | throw new SharedTypeException(String.format("Failed to read custom code snippet from path '%s'", customCodePath), e);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/GoHeaderDataAdaptor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.Context;
4 |
5 | final class GoHeaderDataAdaptor extends AbstractDataAdaptor {
6 | public GoHeaderDataAdaptor(Context ctx) {
7 | super(ctx);
8 | }
9 |
10 | @SuppressWarnings("unused")
11 | String packageName() {
12 | return ctx.getProps().getGo().getOutputFilePackageName();
13 | }
14 |
15 | @Override
16 | String customCodeSnippet() {
17 | return readCustomCodeSnippet(ctx.getProps().getGo().getCustomCodePath());
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/RenderDataAdaptor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | /**
4 | * Implementations of the type is used as render objects feeding to template renderer.
5 | * @author Cause Chung
6 | */
7 | public interface RenderDataAdaptor {
8 | }
9 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/RenderDataAdaptorFactory.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.Context;
4 | import online.sharedtype.processor.support.utils.Tuple;
5 | import online.sharedtype.processor.writer.render.Template;
6 |
7 | public interface RenderDataAdaptorFactory {
8 | Tuple header(Context ctx);
9 |
10 | static Tuple typescript(Context ctx) {
11 | return Tuple.of(Template.TEMPLATE_TYPESCRIPT_HEADER, new TypescriptHeaderDataAdaptor(ctx));
12 | }
13 |
14 | static Tuple rust(Context ctx) {
15 | return Tuple.of(Template.TEMPLATE_RUST_HEADER, new RustHeaderDataAdaptor(ctx));
16 | }
17 |
18 | static Tuple go(Context ctx) {
19 | return Tuple.of(Template.TEMPLATE_GO_HEADER, new GoHeaderDataAdaptor(ctx));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/RustHeaderDataAdaptor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.Context;
4 | import online.sharedtype.processor.context.Props;
5 |
6 | import java.util.LinkedHashSet;
7 | import java.util.Set;
8 |
9 | final class RustHeaderDataAdaptor extends AbstractDataAdaptor {
10 | RustHeaderDataAdaptor(Context ctx) {
11 | super(ctx);
12 | }
13 |
14 | String allowExpr() {
15 | Set allows = new LinkedHashSet<>();
16 | Props.Rust rust = ctx.getProps().getRust();
17 | if (rust.isAllowDeadcode()) {
18 | allows.add("dead_code");
19 | }
20 |
21 | if (!rust.isConvertToSnakeCase()) {
22 | allows.add("non_snake_case");
23 | }
24 |
25 | return allows.isEmpty() ? null : String.format("#![allow(%s)]", String.join(", ", allows));
26 | }
27 |
28 | @Override
29 | String customCodeSnippet() {
30 | return readCustomCodeSnippet(ctx.getProps().getRust().getCustomCodePath());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptor.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.Context;
4 |
5 | final class TypescriptHeaderDataAdaptor extends AbstractDataAdaptor {
6 | TypescriptHeaderDataAdaptor(Context ctx) {
7 | super(ctx);
8 | }
9 |
10 | @Override
11 | String customCodeSnippet() {
12 | return readCustomCodeSnippet(ctx.getProps().getTypescript().getCustomCodePath());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/AbstractEnumConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.domain.def.EnumDef;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 |
6 | abstract class AbstractEnumConverter implements TemplateDataConverter {
7 | @Override
8 | public final boolean shouldAccept(TypeDef typeDef) {
9 | return typeDef instanceof EnumDef && !((EnumDef) typeDef).components().isEmpty();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/AbstractFieldExpr.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.domain.component.ComponentInfo;
5 |
6 | import java.util.List;
7 |
8 | abstract class AbstractFieldExpr {
9 | final String name;
10 | final List tagLiterals;
11 |
12 | AbstractFieldExpr(ComponentInfo componentInfo, SharedType.TargetType targetType) {
13 | name = componentInfo.name();
14 | tagLiterals = componentInfo.getTagLiterals(targetType);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/AbstractStructConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 |
6 | abstract class AbstractStructConverter implements TemplateDataConverter {
7 | @Override
8 | public boolean shouldAccept(TypeDef typeDef) {
9 | if (!(typeDef instanceof ClassDef)) {
10 | return false;
11 | }
12 | ClassDef classDef = (ClassDef) typeDef;
13 | if (classDef.isMapType()) {
14 | return false;
15 | }
16 | return !classDef.components().isEmpty() || classDef.isDepended();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/AbstractTypeExpr.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | /** Represents struct, interface, enum type expression. */
4 | public abstract class AbstractTypeExpr {
5 | }
6 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/ConversionUtils.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.domain.component.FieldComponentInfo;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 |
6 | import java.util.Set;
7 | import java.util.regex.Pattern;
8 |
9 | final class ConversionUtils {
10 | private final static Pattern CAMEL_CASE_PATTERN = Pattern.compile("([a-z])([A-Z]+)");
11 | private ConversionUtils() {}
12 |
13 | static String toSnakeCase(String camelCase) {
14 | return CAMEL_CASE_PATTERN.matcher(camelCase).replaceAll("$1_$2").toLowerCase();
15 | }
16 |
17 | static String capitalize(String str) {
18 | if (str == null || str.isEmpty()) {
19 | return str;
20 | }
21 | return str.substring(0, 1).toUpperCase() + str.substring(1);
22 | }
23 |
24 | static boolean isOptionalField(FieldComponentInfo field) {
25 | if (field.optional()) {
26 | return true;
27 | }
28 | return isOfCyclicReferencedType(field);
29 | }
30 |
31 | static boolean isOfCyclicReferencedType(FieldComponentInfo field) {
32 | if (field.type() instanceof ConcreteTypeInfo) {
33 | ConcreteTypeInfo type = (ConcreteTypeInfo) field.type();
34 | return type.typeDef() != null && type.typeDef().isCyclicReferenced();
35 | }
36 | return false;
37 | }
38 |
39 | static String buildRustMacroTraitsExpr(Set macroTraits) {
40 | if (macroTraits.isEmpty()) {
41 | return null;
42 | }
43 | return String.format("#[derive(%s)]", String.join(", ", macroTraits));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/RustMacroTraitsGenerator.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.domain.def.TypeDef;
4 |
5 | import java.util.Set;
6 |
7 | interface RustMacroTraitsGenerator {
8 | Set generate(TypeDef typeDef);
9 | }
10 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/RustMacroTraitsGeneratorImpl.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.context.Config;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.def.TypeDef;
6 |
7 | import java.util.Collections;
8 | import java.util.LinkedHashSet;
9 | import java.util.Set;
10 |
11 | final class RustMacroTraitsGeneratorImpl implements RustMacroTraitsGenerator {
12 | final Context ctx;
13 | private final Set defaultTraits;
14 |
15 | RustMacroTraitsGeneratorImpl(Context ctx) {
16 | this.ctx = ctx;
17 | this.defaultTraits = ctx.getProps().getRust().getDefaultTypeMacros();
18 | }
19 |
20 | @Override
21 | public Set generate(TypeDef typeDef) {
22 | Config config = ctx.getTypeStore().getConfig(typeDef);
23 | String[] typeMacroTraits = config.getAnno().rustMacroTraits();
24 | Set traits = new LinkedHashSet<>(typeMacroTraits.length + defaultTraits.size());
25 | traits.addAll(defaultTraits);
26 | Collections.addAll(traits, typeMacroTraits);
27 | return traits;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/TemplateDataConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.def.TypeDef;
6 | import online.sharedtype.processor.support.utils.Tuple;
7 | import online.sharedtype.processor.writer.converter.type.TypeExpressionConverter;
8 | import online.sharedtype.processor.writer.render.Template;
9 |
10 | import java.util.HashSet;
11 | import java.util.Set;
12 |
13 | public interface TemplateDataConverter {
14 |
15 | boolean shouldAccept(TypeDef typeDef);
16 |
17 | Tuple convert(TypeDef typeDef);
18 |
19 | static Set typescript(Context ctx) {
20 | Set converters = new HashSet<>(3);
21 | converters.add(new TypescriptInterfaceConverter(ctx, TypeExpressionConverter.typescript(ctx)));
22 | converters.add(new TypescriptEnumConverter(ctx));
23 | converters.add(new ConstantConverter(ctx, TypeExpressionConverter.nullOp(), SharedType.TargetType.TYPESCRIPT));
24 | return converters;
25 | }
26 |
27 | static Set go(Context ctx) {
28 | Set converters = new HashSet<>(3);
29 | TypeExpressionConverter typeExpressionConverter = TypeExpressionConverter.go(ctx);
30 | converters.add(new GoStructConverter(typeExpressionConverter));
31 | converters.add(new GoEnumConverter(ctx, typeExpressionConverter));
32 | converters.add(new ConstantConverter(ctx, typeExpressionConverter, SharedType.TargetType.GO));
33 | return converters;
34 | }
35 |
36 | static Set rust(Context ctx) {
37 | RustMacroTraitsGenerator rustMacroTraitsGenerator = new RustMacroTraitsGeneratorImpl(ctx);
38 | TypeExpressionConverter rustTypeExpressionConverter = TypeExpressionConverter.rust(ctx);
39 | TypeExpressionConverter rustLiteralTypeExpressionConverter = TypeExpressionConverter.rustLiteral(ctx);
40 | Set converters = new HashSet<>(3);
41 | converters.add(new RustStructConverter(ctx, rustTypeExpressionConverter, rustMacroTraitsGenerator));
42 | converters.add(new RustEnumConverter(ctx, rustLiteralTypeExpressionConverter, rustMacroTraitsGenerator));
43 | converters.add(new ConstantConverter(ctx, rustLiteralTypeExpressionConverter, SharedType.TargetType.RUST));
44 | return converters;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/type/RustLiteralTypeExpressionConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter.type;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.Constants;
6 | import online.sharedtype.processor.domain.def.EnumDef;
7 | import online.sharedtype.processor.domain.def.TypeDef;
8 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
9 | import online.sharedtype.processor.domain.type.TypeInfo;
10 | import online.sharedtype.processor.support.exception.SharedTypeInternalError;
11 |
12 | import java.util.HashSet;
13 | import java.util.Set;
14 |
15 | @RequiredArgsConstructor
16 | final class RustLiteralTypeExpressionConverter implements TypeExpressionConverter {
17 | private static final Set STRING_TYPES = new HashSet<>(3);
18 | static {
19 | STRING_TYPES.add(Constants.STRING_TYPE_INFO);
20 | STRING_TYPES.addAll(Constants.MATH_TYPES);
21 | }
22 |
23 | private final Context ctx;
24 |
25 | @Override
26 | public String toTypeExpr(TypeInfo typeInfo, TypeDef contextTypeDef) {
27 | if (!(typeInfo instanceof ConcreteTypeInfo)) {
28 | throw new SharedTypeInternalError(String.format("Literal types must be concrete types, but got: %s in %s", typeInfo, contextTypeDef));
29 | }
30 | ConcreteTypeInfo concreteTypeInfo = (ConcreteTypeInfo) typeInfo;
31 | if (concreteTypeInfo.getKind() == ConcreteTypeInfo.Kind.ENUM) {
32 | EnumDef enumDef = (EnumDef) concreteTypeInfo.typeDef();
33 | if (enumDef.hasComponentValueType() && ctx.getProps().getRust().hasEnumValueTypeAlias()) {
34 | return enumDef.valueTypeAlias();
35 | }
36 | }
37 |
38 | if (STRING_TYPES.contains(concreteTypeInfo)) {
39 | return "&'static str";
40 | }
41 | return RustTypeNameMappings.getOrDefault(concreteTypeInfo, concreteTypeInfo.simpleName());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/type/RustTypeExpressionConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter.type;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.context.Config;
5 | import online.sharedtype.processor.context.Context;
6 | import online.sharedtype.processor.context.RenderFlags;
7 | import online.sharedtype.processor.domain.def.EnumDef;
8 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
9 | import online.sharedtype.processor.domain.Constants;
10 | import online.sharedtype.processor.domain.type.DateTimeInfo;
11 | import online.sharedtype.processor.domain.type.TypeInfo;
12 |
13 | import online.sharedtype.processor.support.annotation.Nullable;
14 |
15 | final class RustTypeExpressionConverter extends AbstractTypeExpressionConverter {
16 | private static final ArraySpec ARRAY_SPEC = new ArraySpec("Vec<", ">");
17 | private static final MapSpec DEFAULT_MAP_SPEC = new MapSpec("HashMap<", ", ", ">");
18 | private final RenderFlags renderFlags;
19 |
20 | RustTypeExpressionConverter(Context ctx) {
21 | super(ctx);
22 | this.renderFlags = ctx.getRenderFlags();
23 | }
24 |
25 | @Override
26 | void beforeVisitTypeInfo(TypeInfo typeInfo) {
27 | if (typeInfo.equals(Constants.OBJECT_TYPE_INFO)) {
28 | renderFlags.setUseRustAny(true);
29 | } else if (typeInfo instanceof ConcreteTypeInfo && ((ConcreteTypeInfo) typeInfo).getKind() == ConcreteTypeInfo.Kind.MAP) {
30 | renderFlags.setUseRustMap(true);
31 | }
32 | }
33 |
34 | @Override
35 | ArraySpec arraySpec() {
36 | return ARRAY_SPEC;
37 | }
38 |
39 | @Override
40 | MapSpec mapSpec(ConcreteTypeInfo typeInfo) {
41 | return DEFAULT_MAP_SPEC;
42 | }
43 |
44 | @Override
45 | String dateTimeTypeExpr(DateTimeInfo dateTimeInfo, Config config) {
46 | return dateTimeInfo.mappedNameOrDefault(SharedType.TargetType.RUST, config.getRustTargetDatetimeTypeLiteral());
47 | }
48 |
49 | @Override
50 | @Nullable
51 | String toTypeExpression(ConcreteTypeInfo typeInfo, @Nullable String defaultExpr) {
52 | String expr = typeInfo.mappedName(SharedType.TargetType.RUST);
53 | if (expr == null) {
54 | expr = RustTypeNameMappings.getOrDefault(typeInfo, defaultExpr);
55 | }
56 | if (expr != null) {
57 | if (typeInfo.typeDef() != null && typeInfo.typeDef().isCyclicReferenced()) {
58 | expr = String.format("Box<%s>", expr);
59 | }
60 | }
61 | return expr;
62 | }
63 |
64 | @Override
65 | TypeInfo mapEnumValueType(ConcreteTypeInfo enumType, EnumDef enumDef) {
66 | return enumDef.getComponentValueType();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/type/RustTypeNameMappings.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter.type;
2 |
3 | import lombok.experimental.UtilityClass;
4 | import online.sharedtype.processor.domain.Constants;
5 | import online.sharedtype.processor.domain.type.TypeInfo;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | @UtilityClass
11 | final class RustTypeNameMappings {
12 | private static final Map typeNameMappings = new HashMap<>(32);
13 | static {
14 | typeNameMappings.put(Constants.BOOLEAN_TYPE_INFO, "bool");
15 | typeNameMappings.put(Constants.BYTE_TYPE_INFO, "i8");
16 | typeNameMappings.put(Constants.CHAR_TYPE_INFO, "char");
17 | typeNameMappings.put(Constants.DOUBLE_TYPE_INFO, "f64");
18 | typeNameMappings.put(Constants.FLOAT_TYPE_INFO, "f32");
19 | typeNameMappings.put(Constants.INT_TYPE_INFO, "i32");
20 | typeNameMappings.put(Constants.LONG_TYPE_INFO, "i64");
21 | typeNameMappings.put(Constants.SHORT_TYPE_INFO, "i16");
22 |
23 | typeNameMappings.put(Constants.BOXED_BOOLEAN_TYPE_INFO, "bool");
24 | typeNameMappings.put(Constants.BOXED_BYTE_TYPE_INFO, "i8");
25 | typeNameMappings.put(Constants.BOXED_CHAR_TYPE_INFO, "char");
26 | typeNameMappings.put(Constants.BOXED_DOUBLE_TYPE_INFO, "f64");
27 | typeNameMappings.put(Constants.BOXED_FLOAT_TYPE_INFO, "f32");
28 | typeNameMappings.put(Constants.BOXED_INT_TYPE_INFO, "i32");
29 | typeNameMappings.put(Constants.BOXED_LONG_TYPE_INFO, "i64");
30 | typeNameMappings.put(Constants.BOXED_SHORT_TYPE_INFO, "i16");
31 | typeNameMappings.put(Constants.BIG_INTEGER_TYPE_INFO, "String");
32 | typeNameMappings.put(Constants.BIG_DECIMAL_TYPE_INFO, "String");
33 |
34 | typeNameMappings.put(Constants.STRING_TYPE_INFO, "String");
35 | typeNameMappings.put(Constants.VOID_TYPE_INFO, "!");
36 | typeNameMappings.put(Constants.OBJECT_TYPE_INFO, "Box");
37 | }
38 |
39 | public static String getOrDefault(TypeInfo typeInfo, String defaultExpr) {
40 | return typeNameMappings.getOrDefault(typeInfo, defaultExpr);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/converter/type/TypeExpressionConverter.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter.type;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import online.sharedtype.processor.context.Context;
5 | import online.sharedtype.processor.domain.def.TypeDef;
6 | import online.sharedtype.processor.domain.type.TypeInfo;
7 | import online.sharedtype.processor.support.annotation.Nullable;
8 |
9 | public interface TypeExpressionConverter {
10 | /** @return null when it's nullOp impl, e.g. typescript constant generation does not need type info. */
11 | @Nullable
12 | String toTypeExpr(TypeInfo typeInfo, TypeDef contextTypeDef);
13 |
14 | static TypeExpressionConverter typescript(Context ctx) {
15 | return new TypescriptTypeExpressionConverter(ctx);
16 | }
17 |
18 | static TypeExpressionConverter go(Context ctx) {
19 | return new GoTypeExpressionConverter(ctx);
20 | }
21 |
22 | static TypeExpressionConverter rust(Context ctx) {
23 | return new RustTypeExpressionConverter(ctx);
24 | }
25 |
26 | static TypeExpressionConverter rustLiteral(Context ctx) {
27 | return new RustLiteralTypeExpressionConverter(ctx);
28 | }
29 |
30 | static TypeExpressionConverter nullOp() {
31 | return (typeInfo, contextTypeDef) -> null;
32 | }
33 |
34 | @RequiredArgsConstructor
35 | final class ArraySpec {
36 | final String prefix;
37 | final String suffix;
38 | }
39 |
40 | @RequiredArgsConstructor
41 | final class MapSpec {
42 | final String prefix;
43 | final String delimiter;
44 | final String suffix;
45 | }
46 |
47 | @RequiredArgsConstructor
48 | final class TypeArgsSpec {
49 | final String prefix;
50 | final String delimiter;
51 | final String suffix;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/render/MustacheTemplateRenderer.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.render;
2 |
3 | import com.github.mustachejava.Mustache;
4 | import com.github.mustachejava.MustacheFactory;
5 | import lombok.RequiredArgsConstructor;
6 | import online.sharedtype.processor.support.exception.SharedTypeInternalError;
7 | import online.sharedtype.processor.support.utils.Tuple;
8 |
9 | import java.io.Writer;
10 | import java.util.List;
11 |
12 | /**
13 | * Implementation via on Mustache .
14 | * The underlying implementation already caches compiled templates.
15 | *
16 | * @author Cause Chung
17 | */
18 | @RequiredArgsConstructor
19 | final class MustacheTemplateRenderer implements TemplateRenderer {
20 | private final MustacheFactory mf;
21 |
22 | @Override
23 | public void render(Writer writer, List> data) {
24 | for (Tuple tuple : data) {
25 | Template template = tuple.a();
26 | Object values = tuple.b();
27 | Mustache mustache = mf.compile(template.getResourcePath());
28 | if (mustache == null) {
29 | throw new SharedTypeInternalError(String.format("Template not found: '%s'", template));
30 | }
31 | mustache.execute(writer, values);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/render/Template.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.render;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.Getter;
5 | import online.sharedtype.SharedType;
6 |
7 | /**
8 | * Represents a template used by {@link TemplateRenderer}.
9 | *
10 | * @author Cause Chung
11 | */
12 | @EqualsAndHashCode
13 | @Getter
14 | public final class Template {
15 | public static final Template TEMPLATE_TYPESCRIPT_HEADER = new Template(SharedType.TargetType.TYPESCRIPT, "header");
16 | public static final Template TEMPLATE_TYPESCRIPT_INTERFACE = new Template(SharedType.TargetType.TYPESCRIPT, "interface");
17 | public static final Template TEMPLATE_TYPESCRIPT_UNION_TYPE_ENUM = new Template(SharedType.TargetType.TYPESCRIPT, "union-type-enum");
18 | public static final Template TEMPLATE_TYPESCRIPT_ENUM = new Template(SharedType.TargetType.TYPESCRIPT, "enum");
19 | public static final Template TEMPLATE_RUST_HEADER = new Template(SharedType.TargetType.RUST, "header");
20 | public static final Template TEMPLATE_RUST_STRUCT = new Template(SharedType.TargetType.RUST, "struct");
21 | public static final Template TEMPLATE_RUST_ENUM = new Template(SharedType.TargetType.RUST, "enum");
22 | public static final Template TEMPLATE_GO_HEADER = new Template(SharedType.TargetType.GO, "header");
23 | public static final Template TEMPLATE_GO_STRUCT = new Template(SharedType.TargetType.GO, "struct");
24 | public static final Template TEMPLATE_GO_CONST_ENUM = new Template(SharedType.TargetType.GO, "const-enum");
25 | public static final Template TEMPLATE_GO_STRUCT_ENUM = new Template(SharedType.TargetType.GO, "struct-enum");
26 |
27 | private final SharedType.TargetType targetType;
28 | private final String resourcePath;
29 |
30 | Template(SharedType.TargetType targetType, String resourceName) {
31 | this.targetType = targetType;
32 | this.resourcePath = String.format("templates/%s/%s.mustache", targetType.name().toLowerCase(), resourceName);
33 | }
34 |
35 | public static Template forConstant(SharedType.TargetType targetType, boolean constantNamespaced) {
36 | return new Template(targetType, constantNamespaced ? "constant" : "constant-inline");
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | return resourcePath;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/processor/src/main/java/online/sharedtype/processor/writer/render/TemplateRenderer.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.render;
2 |
3 | import com.github.mustachejava.DefaultMustacheFactory;
4 | import online.sharedtype.processor.support.utils.Tuple;
5 |
6 | import java.io.Writer;
7 | import java.util.List;
8 |
9 | /**
10 | *
11 | * @author Cause Chung
12 | */
13 | public interface TemplateRenderer {
14 |
15 | /**
16 | * Renders the target output to the writer specified.
17 | *
18 | * @param writer java.io.Writer
19 | * @param data a list of tuple containing the template and corresponding data for rendering.
20 | */
21 | void render(Writer writer, List> data);
22 |
23 | static TemplateRenderer create() {
24 | return new MustacheTemplateRenderer(new DefaultMustacheFactory());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/const-enum.mustache:
--------------------------------------------------------------------------------
1 | type {{name}} = {{valueType}}
2 | const (
3 | {{#enumerations}}
4 | {{#tagLiterals}}
5 | {{{.}}}
6 | {{/tagLiterals}}
7 | {{name}} {{enumName}} = {{{value}}}
8 | {{/enumerations}}
9 | )
10 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/constant-inline.mustache:
--------------------------------------------------------------------------------
1 | const (
2 | {{#constants}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | {{name}} {{{type}}} = {{{value}}};
7 | {{/constants}}
8 | )
9 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/constant.mustache:
--------------------------------------------------------------------------------
1 | var {{name}} = struct {
2 | {{#constants}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | {{name}} {{type}}
7 | {{/constants}}
8 | }{
9 | {{#constants}}
10 | {{{value}}},
11 | {{/constants}}
12 | }
13 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/header.mustache:
--------------------------------------------------------------------------------
1 | // Code generated by https://github.com/SharedType/sharedtype
2 | package {{packageName}}
3 |
4 | {{{customCodeSnippet}}}
5 |
6 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/struct-enum.mustache:
--------------------------------------------------------------------------------
1 | type {{name}} = {{valueType}}
2 | var {{name}}Enums = struct {
3 | {{#enumerations}}
4 | {{#tagLiterals}}
5 | {{{.}}}
6 | {{/tagLiterals}}
7 | {{name}} {{enumName}}
8 | {{/enumerations}}
9 | }{
10 | {{#enumerations}}
11 | {{{value}}},
12 | {{/enumerations}}
13 | }
14 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/go/struct.mustache:
--------------------------------------------------------------------------------
1 | type {{name}}{{typeParametersExpr}} struct {
2 | {{#supertypes}}
3 | {{.}}
4 | {{/supertypes}}
5 | {{#properties}}
6 | {{#tagLiterals}}
7 | {{{.}}}
8 | {{/tagLiterals}}
9 | {{capitalizedName}} {{{typeExpr}}} `{{{tagsExpr}}}`
10 | {{/properties}}
11 | }
12 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/rust/constant-inline.mustache:
--------------------------------------------------------------------------------
1 | {{#constants}}
2 | {{#tagLiterals}}
3 | {{{.}}}
4 | {{/tagLiterals}}
5 | pub const {{name}}: {{{type}}} = {{{value}}};
6 | {{/constants}}
7 |
8 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/rust/constant.mustache:
--------------------------------------------------------------------------------
1 | pub mod {{name}} {
2 | {{#constants}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | pub const {{name}}: {{{type}}} = {{{value}}};
7 | {{/constants}}
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/rust/enum.mustache:
--------------------------------------------------------------------------------
1 | {{macroTraitsExpr}}
2 | pub enum {{name}} {
3 | {{#enumerations}}
4 | {{#tagLiterals}}
5 | {{{.}}}
6 | {{/tagLiterals}}
7 | {{name}},
8 | {{/enumerations}}
9 | }
10 | {{#hasValue}}
11 | {{#valueTypeAlias}}
12 | pub type {{valueTypeAlias}} = {{{valueType}}};
13 | {{/valueTypeAlias}}
14 | impl {{name}} {
15 | pub const fn value(self) -> {{valueTypeAlias}} {
16 | use {{name}}::*;
17 | match self {
18 | {{#enumerations}}
19 | {{name}} => {{{value}}},
20 | {{/enumerations}}
21 | }
22 | }
23 | }
24 | {{/hasValue}}
25 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/rust/header.mustache:
--------------------------------------------------------------------------------
1 | // Code generated by https://github.com/SharedType/sharedtype
2 | {{allowExpr}}
3 | {{#renderFlags.useRustAny}}use std::any::Any;{{/renderFlags.useRustAny}}
4 | {{#renderFlags.useRustMap}}use std::collections::HashMap;{{/renderFlags.useRustMap}}
5 |
6 | {{{customCodeSnippet}}}
7 |
8 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/rust/struct.mustache:
--------------------------------------------------------------------------------
1 | {{macroTraitsExpr}}
2 | pub struct {{name}}{{{typeParametersExpr}}} {
3 | {{#properties}}
4 | {{#tagLiterals}}
5 | {{{.}}}
6 | {{/tagLiterals}}
7 | pub {{name}}: {{{typeExpr}}},
8 | {{/properties}}
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/constant-inline.mustache:
--------------------------------------------------------------------------------
1 | {{#constants}}
2 | {{#tagLiterals}}
3 | {{{.}}}
4 | {{/tagLiterals}}
5 | export const {{name}} = {{{value}}};
6 | {{/constants}}
7 |
8 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/constant.mustache:
--------------------------------------------------------------------------------
1 | export const {{name}} = {
2 | {{#constants}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | {{name}}: {{{value}}},
7 | {{/constants}}
8 | } as const;
9 |
10 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/enum.mustache:
--------------------------------------------------------------------------------
1 | export {{#isConst}}const {{/isConst}}enum {{name}} {
2 | {{#values}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | {{name}} = {{{value}}},
7 | {{/values}}
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/header.mustache:
--------------------------------------------------------------------------------
1 | // Code generated by https://github.com/SharedType/sharedtype
2 |
3 | {{{customCodeSnippet}}}
4 |
5 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/interface.mustache:
--------------------------------------------------------------------------------
1 | export interface {{name}}{{{typeParametersExpr}}} {{{supertypesExpr}}}{
2 | {{#properties}}
3 | {{#tagLiterals}}
4 | {{{.}}}
5 | {{/tagLiterals}}
6 | {{#readonly}}readonly {{/readonly}}{{name}}{{#optional}}?{{/optional}}: {{{type}}}{{#unionNull}} | null{{/unionNull}}{{#unionUndefined}} | undefined{{/unionUndefined}}{{propDelimiter}}
7 | {{/properties}}
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/processor/src/main/resources/templates/typescript/union-type-enum.mustache:
--------------------------------------------------------------------------------
1 | export type {{name}} = {{{valuesExpr}}};
2 |
3 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/AnnotationProcessorImplTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.Constants;
5 | import online.sharedtype.processor.context.ContextMocks;
6 | import org.junit.jupiter.api.BeforeEach;
7 | import org.junit.jupiter.api.Test;
8 | import org.mockito.ArgumentCaptor;
9 | import online.sharedtype.processor.parser.TypeDefParser;
10 | import online.sharedtype.processor.resolver.TypeResolver;
11 | import online.sharedtype.processor.writer.TypeWriter;
12 |
13 | import java.util.Collections;
14 | import java.util.List;
15 | import java.util.Set;
16 |
17 | import static org.assertj.core.api.Assertions.assertThat;
18 | import static org.mockito.ArgumentMatchers.eq;
19 | import static org.mockito.Mockito.mock;
20 | import static org.mockito.Mockito.verify;
21 | import static org.mockito.Mockito.when;
22 |
23 | class AnnotationProcessorImplTest {
24 | private final ContextMocks ctxMocks = new ContextMocks();
25 | private final TypeDefParser typeDefParser = mock(TypeDefParser.class);
26 | private final TypeResolver typeResolver = mock(TypeResolver.class);
27 | private final TypeWriter typeWriter = mock(TypeWriter.class);
28 | private final AnnotationProcessorImpl processor = new AnnotationProcessorImpl();
29 |
30 | private final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class);
31 |
32 | @BeforeEach
33 | void setUp() {
34 | processor.ctx = ctxMocks.getContext();
35 | processor.parser = typeDefParser;
36 | processor.resolver = typeResolver;
37 | processor.writer = typeWriter;
38 | }
39 |
40 | @Test
41 | void doProcess() throws Exception {
42 | var typeElement1 = ctxMocks.typeElement("com.github.cuzfrog.Abc").element();
43 | var typeElement2 = ctxMocks.typeElement("com.github.cuzfrog.IgnoredClass").element();
44 | var classDef1 = ClassDef.builder().qualifiedName("com.github.cuzfrog.Abc").simpleName("Abc").build();
45 | when(typeDefParser.parse(typeElement1)).thenReturn(Collections.singletonList(classDef1));
46 | when(typeDefParser.parse(typeElement2)).thenReturn(Collections.emptyList());
47 |
48 | var dependencyDef = ClassDef.builder().qualifiedName("com.github.cuzfrog.Dependency").simpleName("Dependency").build();
49 | when(typeResolver.resolve(List.of(classDef1))).thenReturn(List.of(classDef1, dependencyDef));
50 |
51 | processor.doProcess(Set.of(typeElement1, typeElement2));
52 |
53 | verify(typeWriter).write(List.of(classDef1, dependencyDef));
54 | verify(ctxMocks.getContext()).warn(eq(typeElement2), messageCaptor.capture(), eq("com.github.cuzfrog.IgnoredClass"), eq(Constants.ANNOTATION_QUALIFIED_NAME));
55 | assertThat(messageCaptor.getValue()).contains("is ignored or invalid");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/AbstractTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.Tree;
4 | import lombok.Getter;
5 |
6 | import javax.lang.model.element.Element;
7 |
8 | import static org.mockito.Mockito.when;
9 |
10 | public abstract class AbstractTreeMock> {
11 | final Context ctx;
12 | @Getter
13 | final T tree;
14 |
15 | AbstractTreeMock(T tree, Context ctx) {
16 | this.ctx = ctx;
17 | this.tree = tree;
18 | }
19 |
20 | void fromElement(Element element) {
21 | when(ctx.getTrees().getTree(element)).thenReturn(tree);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/AnnotationMirrorMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.AnnotationMirror;
4 | import javax.lang.model.type.DeclaredType;
5 |
6 | import static org.mockito.Mockito.mock;
7 | import static org.mockito.Mockito.when;
8 |
9 | public final class AnnotationMirrorMock {
10 | private final AnnotationMirror mocked = mock(AnnotationMirror.class);
11 |
12 | AnnotationMirrorMock(DeclaredType declaredType) {
13 | when(mocked.getAnnotationType()).thenReturn(declaredType);
14 | }
15 |
16 | public AnnotationMirror mocked() {
17 | return mocked;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/ArrayTypeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.type.ArrayType;
4 | import javax.lang.model.type.TypeKind;
5 | import javax.lang.model.type.TypeMirror;
6 |
7 | import static org.mockito.Mockito.mock;
8 | import static org.mockito.Mockito.when;
9 |
10 | public final class ArrayTypeMock {
11 | private final ArrayType type;
12 |
13 | ArrayTypeMock(TypeMirror componentType) {
14 | type = mock(ArrayType.class);
15 | when(type.getComponentType()).thenReturn(componentType);
16 | when(type.getKind()).thenReturn(TypeKind.ARRAY);
17 | }
18 |
19 | public ArrayType type() {
20 | return type;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/AssignmentTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.AssignmentTree;
4 | import com.sun.source.tree.ExpressionTree;
5 | import com.sun.source.tree.Tree;
6 | import com.sun.source.tree.VariableTree;
7 |
8 | import static org.mockito.Mockito.mock;
9 | import static org.mockito.Mockito.when;
10 |
11 | public final class AssignmentTreeMock extends AbstractTreeMock {
12 | AssignmentTreeMock(Context ctx) {
13 | super(mock(AssignmentTree.class), ctx);
14 | when(tree.getKind()).thenReturn(Tree.Kind.ASSIGNMENT);
15 | }
16 |
17 | public AssignmentTreeMock withVariable(ExpressionTree variableTree) {
18 | when(tree.getVariable()).thenReturn(variableTree);
19 | return this;
20 | }
21 |
22 | public AssignmentTreeMock withExpression(ExpressionTree expressionTree) {
23 | when(tree.getExpression()).thenReturn(expressionTree);
24 | return this;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/DeclaredTypeVariableElementMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.VariableElement;
4 | import javax.lang.model.type.DeclaredType;
5 | import javax.lang.model.type.TypeKind;
6 | import javax.lang.model.util.Types;
7 |
8 | import static org.mockito.Mockito.mock;
9 | import static org.mockito.Mockito.when;
10 |
11 | public final class DeclaredTypeVariableElementMock extends AbstractElementMock {
12 | DeclaredTypeVariableElementMock(String name, DeclaredType declaredType, Context ctx) {
13 | super(mock(VariableElement.class, name), declaredType, ctx);
14 | setSimpleName(element, name);
15 | }
16 |
17 | public DeclaredTypeVariableElementMock withTypeKind(TypeKind typeKind) {
18 | when(type.getKind()).thenReturn(typeKind);
19 | return this;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/ExecutableElementMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.ElementKind;
4 | import javax.lang.model.element.ExecutableElement;
5 | import javax.lang.model.element.VariableElement;
6 | import javax.lang.model.type.ExecutableType;
7 | import javax.lang.model.type.TypeKind;
8 | import javax.lang.model.type.TypeMirror;
9 | import javax.lang.model.util.Types;
10 |
11 | import java.util.Arrays;
12 |
13 | import static org.mockito.Mockito.mock;
14 | import static org.mockito.Mockito.when;
15 |
16 | public final class ExecutableElementMock extends AbstractElementMock {
17 | private static final ElementKind DEFAULT_ELEMENT_KIND = ElementKind.METHOD;
18 |
19 | ExecutableElementMock(String name, Context ctx) {
20 | super(mock(ExecutableElement.class, name), mock(ExecutableType.class, name), ctx);
21 | setSimpleName(element, name);
22 | when(type.getKind()).thenReturn(TypeKind.EXECUTABLE);
23 | when(element.getKind()).thenReturn(DEFAULT_ELEMENT_KIND);
24 | }
25 |
26 | public ExecutableElementMock withReturnType(TypeMirror returnType) {
27 | when(element.getReturnType()).thenReturn(returnType);
28 | when(type.getReturnType()).thenReturn(returnType);
29 | return this;
30 | }
31 |
32 | public ExecutableElementMock withParameters(VariableElement... parameters) {
33 | when(element.getParameters()).then(invoc -> Arrays.asList(parameters));
34 | return this;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/ExpressionTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.ExpressionTree;
4 |
5 | public abstract class ExpressionTreeMock> extends AbstractTreeMock {
6 | ExpressionTreeMock(T tree, Context ctx) {
7 | super(tree, ctx);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/IdentifierTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.IdentifierTree;
4 | import com.sun.source.tree.Tree;
5 |
6 | import javax.lang.model.element.Name;
7 |
8 | import static org.mockito.Mockito.mock;
9 | import static org.mockito.Mockito.when;
10 |
11 | public final class IdentifierTreeMock extends ExpressionTreeMock {
12 | IdentifierTreeMock(String name, Context ctx) {
13 | super(mock(IdentifierTree.class, String.format("Tree(%s)", name)), ctx);
14 | Name elementName = new MockName(name);
15 | when(tree.getName()).thenReturn(elementName);
16 | when(tree.getKind()).thenReturn(Tree.Kind.IDENTIFIER);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/LiteralTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.LiteralTree;
4 | import com.sun.source.tree.Tree;
5 |
6 | import static org.mockito.Mockito.mock;
7 | import static org.mockito.Mockito.when;
8 |
9 | public final class LiteralTreeMock extends ExpressionTreeMock {
10 | LiteralTreeMock(Object value, Context ctx) {
11 | super(mock(LiteralTree.class), ctx);
12 | when(tree.getValue()).thenReturn(value);
13 | when(tree.getKind()).thenReturn(getKind(value));
14 | }
15 |
16 | private static Tree.Kind getKind(Object value) {
17 | return switch (value) {
18 | case String ignored -> Tree.Kind.STRING_LITERAL;
19 | case Integer ignored -> Tree.Kind.INT_LITERAL;
20 | case Long ignored -> Tree.Kind.LONG_LITERAL;
21 | case Float ignored -> Tree.Kind.FLOAT_LITERAL;
22 | case Double ignored -> Tree.Kind.DOUBLE_LITERAL;
23 | case Character ignored -> Tree.Kind.CHAR_LITERAL;
24 | case Boolean ignored -> Tree.Kind.BOOLEAN_LITERAL;
25 | case null -> Tree.Kind.NULL_LITERAL;
26 | default -> throw new IllegalArgumentException("Unsupported literal type: " + value.getClass());
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/MemberSelectTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.ExpressionTree;
4 | import com.sun.source.tree.MemberSelectTree;
5 | import com.sun.source.tree.Tree;
6 |
7 | import static org.mockito.Mockito.mock;
8 | import static org.mockito.Mockito.when;
9 |
10 | public final class MemberSelectTreeMock extends ExpressionTreeMock {
11 | MemberSelectTreeMock(String identifier, Context ctx) {
12 | super(mock(MemberSelectTree.class), ctx);
13 | when(tree.getIdentifier()).thenReturn(new MockName(identifier));
14 | when(tree.toString()).thenReturn(identifier);
15 | when(tree.getKind()).thenReturn(Tree.Kind.MEMBER_SELECT);
16 | }
17 |
18 | public MemberSelectTreeMock withExpression(ExpressionTree expressionTree) {
19 | when(tree.getExpression()).thenReturn(expressionTree);
20 | return this;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/MockName.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.RequiredArgsConstructor;
5 |
6 | import javax.lang.model.element.Name;
7 |
8 | @EqualsAndHashCode
9 | @RequiredArgsConstructor
10 | final class MockName implements Name {
11 | private final String name;
12 |
13 | @Override
14 | public boolean contentEquals(CharSequence cs) {
15 | return name.contentEquals(cs);
16 | }
17 | @Override
18 | public int length() {
19 | return name.length();
20 | }
21 | @Override
22 | public char charAt(int index) {
23 | return name.charAt(index);
24 | }
25 | @Override
26 | public CharSequence subSequence(int start, int end) {
27 | return name.subSequence(start, end);
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return name;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/NewClassTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.ExpressionTree;
4 | import com.sun.source.tree.NewClassTree;
5 | import com.sun.source.tree.Tree;
6 |
7 | import java.util.Arrays;
8 | import java.util.stream.Collectors;
9 |
10 | import static org.mockito.Mockito.mock;
11 | import static org.mockito.Mockito.when;
12 |
13 | public final class NewClassTreeMock extends ExpressionTreeMock {
14 | NewClassTreeMock(Context ctx) {
15 | super(mock(NewClassTree.class), ctx);
16 | when(tree.getKind()).thenReturn(NewClassTree.Kind.NEW_CLASS);
17 | }
18 |
19 | public NewClassTreeMock withArguments(ExpressionTree... arguments) {
20 | when(tree.getArguments()).then(invoc -> Arrays.stream(arguments).collect(Collectors.toList()));
21 | return this;
22 | }
23 |
24 | public NewClassTreeMock withIdentifier(ExpressionTree identifier) {
25 | when(tree.getIdentifier()).thenReturn(identifier);
26 | return this;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/PackageElementMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.PackageElement;
4 | import javax.lang.model.type.NoType;
5 |
6 | import static org.mockito.Mockito.mock;
7 | import static org.mockito.Mockito.when;
8 |
9 | public final class PackageElementMock extends AbstractElementMock {
10 | PackageElementMock(String qualifiedName, Context ctx) {
11 | super(mock(PackageElement.class, qualifiedName), mock(NoType.class), ctx);
12 | setQualifiedName(element, qualifiedName);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/PrimitiveVariableElementMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.ElementKind;
4 | import javax.lang.model.element.VariableElement;
5 | import javax.lang.model.type.PrimitiveType;
6 | import javax.lang.model.type.TypeKind;
7 | import javax.lang.model.util.Types;
8 |
9 | import static org.assertj.core.api.Assertions.assertThat;
10 | import static org.mockito.Mockito.mock;
11 | import static org.mockito.Mockito.when;
12 |
13 | public final class PrimitiveVariableElementMock extends AbstractElementMock {
14 | PrimitiveVariableElementMock(String name, TypeKind typeKind, Context ctx) {
15 | super(mock(VariableElement.class, name), mock(PrimitiveType.class, typeKind.name()), ctx);
16 | assertThat(typeKind.isPrimitive()).isTrue();
17 | setSimpleName(element, name);
18 | when(element.getKind()).thenReturn(ElementKind.FIELD);
19 | when(type.getKind()).thenReturn(typeKind);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/RecordComponentMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.ElementKind;
4 | import javax.lang.model.element.ExecutableElement;
5 | import javax.lang.model.element.RecordComponentElement;
6 | import javax.lang.model.type.TypeMirror;
7 | import javax.lang.model.util.Types;
8 |
9 | import static org.mockito.Mockito.mock;
10 | import static org.mockito.Mockito.when;
11 |
12 | public final class RecordComponentMock extends AbstractElementMock> {
13 | RecordComponentMock(String name, T type, Context ctx) {
14 | super(mock(RecordComponentElement.class, name), type, ctx);
15 | setSimpleName(element, name);
16 | when(element.asType()).thenReturn(type);
17 | when(element.getKind()).thenReturn(ElementKind.RECORD_COMPONENT);
18 | }
19 |
20 | public RecordComponentMock withAccessor(ExecutableElement accessor) {
21 | when(element.getAccessor()).thenReturn(accessor);
22 | return this;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/TestUtils.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import lombok.experimental.UtilityClass;
4 | import online.sharedtype.SharedType;
5 |
6 | import static org.mockito.Mockito.spy;
7 |
8 | @UtilityClass
9 | public final class TestUtils {
10 | public static SharedType defaultSharedTypeAnnotation() {
11 | return spy(Config.DummyDefault.class.getAnnotation(Config.AnnoContainer.class).anno());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/TypeParameterElementMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import javax.lang.model.element.TypeParameterElement;
4 | import javax.lang.model.type.TypeKind;
5 | import javax.lang.model.type.TypeVariable;
6 | import javax.lang.model.util.Types;
7 |
8 | import static org.mockito.Mockito.mock;
9 | import static org.mockito.Mockito.when;
10 |
11 | public final class TypeParameterElementMock extends AbstractElementMock {
12 | TypeParameterElementMock(String name, Context ctx) {
13 | super(mock(TypeParameterElement.class), mock(TypeVariable.class), ctx);
14 | when(type.getKind()).thenReturn(TypeKind.TYPEVAR);
15 | when(type.asElement()).thenReturn(element);
16 | when(types.asElement(type)).thenReturn(element);
17 | setSimpleName(element, name);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/context/VariableTreeMock.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.context;
2 |
3 | import com.sun.source.tree.ExpressionTree;
4 | import com.sun.source.tree.Tree;
5 | import com.sun.source.tree.VariableTree;
6 |
7 | import static org.mockito.Mockito.mock;
8 | import static org.mockito.Mockito.when;
9 |
10 | public final class VariableTreeMock extends AbstractTreeMock {
11 | VariableTreeMock(Context ctx) {
12 | super(mock(VariableTree.class), ctx);
13 | when(tree.getKind()).thenReturn(VariableTree.Kind.VARIABLE);
14 | }
15 |
16 | public VariableTreeMock withInitializer(ExpressionTree initializer) {
17 | when(tree.getInitializer()).thenReturn(initializer);
18 | return this;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/domain/TypeEqualityTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.domain;
2 |
3 | import online.sharedtype.processor.domain.type.ArrayTypeInfo;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 | import online.sharedtype.processor.domain.type.TypeInfo;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import java.util.List;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 |
12 | final class TypeEqualityTest {
13 | @Test
14 | void differentTypeOfTypeInfoShouldNotEqual() {
15 | TypeInfo type1 = ConcreteTypeInfo.builder().qualifiedName("java.lang.String").build();
16 | TypeInfo arr = new ArrayTypeInfo(type1);
17 | assertThat(arr).isNotEqualTo(type1);
18 | }
19 |
20 | @Test
21 | void arrayWithSameComponentTypeShouldEqual() {
22 | TypeInfo type1 = ConcreteTypeInfo.builder().qualifiedName("java.lang.String").build();
23 | TypeInfo arr1 = new ArrayTypeInfo(type1);
24 | TypeInfo arr2 = new ArrayTypeInfo(type1);
25 | assertThat(arr1).isEqualTo(arr2);
26 | }
27 |
28 | @Test
29 | void genericTypeWithDifferentTypeArgumentsShouldNotEqual() {
30 | TypeInfo type1 = ConcreteTypeInfo.builder().qualifiedName("java.util.List").typeArgs(List.of(
31 | ConcreteTypeInfo.builder().qualifiedName("java.lang.String").build()
32 | )).build();
33 | TypeInfo type2 = ConcreteTypeInfo.builder().qualifiedName("java.util.List").typeArgs(List.of(
34 | ConcreteTypeInfo.builder().qualifiedName("java.lang.Integer").build()
35 | )).build();
36 | assertThat(type1).isNotEqualTo(type2);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/domain/def/EnumDefTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.def;
2 |
3 | import online.sharedtype.processor.domain.Constants;
4 | import online.sharedtype.processor.domain.component.EnumValueInfo;
5 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
6 | import online.sharedtype.processor.domain.value.ValueHolder;
7 | import org.junit.jupiter.api.Test;
8 | import org.junit.jupiter.api.TestInstance;
9 |
10 | import java.util.Collections;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 |
14 | @TestInstance(TestInstance.Lifecycle.PER_CLASS)
15 | final class EnumDefTest {
16 | private final ConcreteTypeInfo enumTypeInfo = ConcreteTypeInfo.builder().simpleName("EnumA").kind(ConcreteTypeInfo.Kind.ENUM).build();
17 |
18 | @Test
19 | void componentValueTypeIsNullForEmptyEnum() {
20 | EnumDef enumDef = EnumDef.builder()
21 | .qualifiedName("com.github.cuzfrog.EnumA").simpleName("EnumA")
22 | .enumValueInfos(Collections.emptyList())
23 | .typeInfo(enumTypeInfo)
24 | .build();
25 | assertThat(enumDef.getComponentValueType()).isEqualTo(enumTypeInfo);
26 | }
27 |
28 | @Test
29 | void componentValueType() {
30 | EnumDef enumDef = EnumDef.builder()
31 | .qualifiedName("com.github.cuzfrog.EnumA").simpleName("EnumA")
32 | .enumValueInfos(Collections.singletonList(
33 | EnumValueInfo.builder().name("Value1")
34 | .value(ValueHolder.ofEnum(enumTypeInfo, "Value1", Constants.BOOLEAN_TYPE_INFO, true))
35 | .build()
36 | ))
37 | .typeInfo(enumTypeInfo)
38 | .build();
39 | assertThat(enumDef.getComponentValueType()).isEqualTo(Constants.BOOLEAN_TYPE_INFO);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/domain/type/ReferableTypeInfoTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.type;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.assertj.core.api.Assertions.assertThat;
7 |
8 | final class ReferableTypeInfoTest {
9 | @Test
10 | void arrayNestedReferencedType() {
11 | var concreteTypeInfo = ConcreteTypeInfo.builder().build();
12 | var wrapped = new ArrayTypeInfo(concreteTypeInfo);
13 |
14 | var typeDef = ClassDef.builder().build();
15 | wrapped.addReferencingType(typeDef);
16 |
17 | assertThat(concreteTypeInfo.referencingTypes()).containsExactly(typeDef);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/domain/value/ValueHolderTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.domain.value;
2 |
3 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static online.sharedtype.processor.domain.Constants.INT_TYPE_INFO;
7 | import static online.sharedtype.processor.domain.Constants.STRING_TYPE_INFO;
8 | import static org.assertj.core.api.Assertions.assertThat;
9 |
10 | final class ValueHolderTest {
11 |
12 | @Test
13 | void literalValue() {
14 | assertThat(ValueHolder.of(STRING_TYPE_INFO, "abc").literalValue()).isEqualTo("\"abc\"");
15 | assertThat(ValueHolder.of(INT_TYPE_INFO, 105).literalValue()).isEqualTo("105");
16 | }
17 |
18 | @Test
19 | void getNestedValue() {
20 | var enumType = ConcreteTypeInfo.builder().qualifiedName("com.github.cuzfrog.EnumA").build();
21 | var value = ValueHolder.of(
22 | null,
23 | ValueHolder.of(
24 | enumType,
25 | ValueHolder.ofEnum(
26 | enumType,
27 | "ENUM_CONST",
28 | STRING_TYPE_INFO,
29 | ValueHolder.of(STRING_TYPE_INFO, ValueHolder.of(STRING_TYPE_INFO, "Value1"))
30 | )
31 | )
32 | );
33 | assertThat(value.getValue()).isEqualTo("Value1");
34 | }
35 |
36 | @Test
37 | void equalities() {
38 | assertThat(ValueHolder.of(STRING_TYPE_INFO, "abc"))
39 | .isEqualTo(ValueHolder.of(STRING_TYPE_INFO, ValueHolder.of(STRING_TYPE_INFO,"abc")));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/parser/type/MappableTypeInfoParserTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.type;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.context.ContextMocks;
5 | import online.sharedtype.processor.domain.type.DateTimeInfo;
6 | import org.junit.jupiter.api.Test;
7 | import org.junitpioneer.jupiter.SetSystemProperty;
8 |
9 | import javax.lang.model.element.TypeElement;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 | import static org.mockito.Mockito.mock;
13 | import static org.mockito.Mockito.when;
14 |
15 | @SetSystemProperty(key = "sharedtype.typescript.type-mappings", value = "a.b.MyDateTime:MyString")
16 | @SetSystemProperty(key = "sharedtype.go.type-mappings", value = "a.b.MyDateTime:MyStringG")
17 | @SetSystemProperty(key = "sharedtype.rust.type-mappings", value = "a.b.MyDateTime:MyStringR")
18 | final class MappableTypeInfoParserTest {
19 | private final ContextMocks ctxMocks = new ContextMocks();
20 | private final TypeInfoParser delegate = mock(TypeInfoParser.class);
21 | private final MappableTypeInfoParser parser = new MappableTypeInfoParser(ctxMocks.getContext(), delegate);
22 |
23 | @Test
24 | void addTypeMappings() {
25 | TypeElement typeElement1 = ctxMocks.typeElement("a.b.MyDateTime").element();
26 |
27 | DateTimeInfo dateTimeInfo = new DateTimeInfo("a.b.MyDateTime");
28 | when(delegate.parse(typeElement1.asType(), typeElement1)).thenReturn(dateTimeInfo);
29 |
30 | var resTypeInfo = parser.parse(typeElement1.asType(), typeElement1);
31 | assertThat(resTypeInfo).isSameAs(dateTimeInfo);
32 | assertThat(dateTimeInfo.mappedNameOrDefault(SharedType.TargetType.TYPESCRIPT, "DefaultName")).isEqualTo("MyString");
33 | assertThat(dateTimeInfo.mappedNameOrDefault(SharedType.TargetType.GO, "DefaultName")).isEqualTo("MyStringG");
34 | assertThat(dateTimeInfo.mappedNameOrDefault(SharedType.TargetType.RUST, "DefaultName")).isEqualTo("MyStringR");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/parser/value/ConstantValueParserTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.parser.value;
2 |
3 | import online.sharedtype.processor.context.ContextMocks;
4 | import online.sharedtype.processor.domain.Constants;
5 | import online.sharedtype.processor.parser.type.TypeInfoParser;
6 | import org.junit.jupiter.api.Test;
7 | import org.mockito.ArgumentCaptor;
8 |
9 | import javax.lang.model.type.TypeKind;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 | import static org.mockito.ArgumentMatchers.any;
13 | import static org.mockito.Mockito.mock;
14 | import static org.mockito.Mockito.verify;
15 | import static org.mockito.Mockito.when;
16 |
17 | final class ConstantValueParserTest {
18 | private final ContextMocks ctxMocks = new ContextMocks();
19 | private final TypeInfoParser typeInfoParser = mock(TypeInfoParser.class);
20 | private final ValueResolverBackend valueResolverBackend = mock(ValueResolverBackend.class);
21 | private final ConstantValueParser resolver = new ConstantValueParser(ctxMocks.getContext(), typeInfoParser, valueResolverBackend);
22 |
23 | private final ArgumentCaptor valueResolveContextCaptor = ArgumentCaptor.forClass(ValueResolveContext.class);
24 |
25 | @Test
26 | void resolve() {
27 | var typeElement = ctxMocks.typeElement("com.github.cuzfrog.Abc").element();
28 | var fieldTree = ctxMocks.variableTree().withInitializer(ctxMocks.literalTree(123).getTree());
29 | var fieldElement = ctxMocks.primitiveVariable("field1", TypeKind.INT)
30 | .withEnclosingElement(typeElement)
31 | .ofTree(fieldTree)
32 | .element();
33 |
34 | when(typeInfoParser.parse(fieldElement.asType(), typeElement)).thenReturn(Constants.INT_TYPE_INFO);
35 | when(valueResolverBackend.recursivelyResolve(any())).thenReturn(123);
36 |
37 | var value = resolver.resolve(fieldElement, typeElement);
38 | assertThat(value.getValue()).isEqualTo(123);
39 | assertThat(value.getValueType()).isEqualTo(Constants.INT_TYPE_INFO);
40 |
41 | verify(valueResolverBackend).recursivelyResolve(valueResolveContextCaptor.capture());
42 | var valueResolveContext = valueResolveContextCaptor.getValue();
43 | assertThat(valueResolveContext.getEnclosingTypeElement()).isEqualTo(typeElement);
44 | assertThat(valueResolveContext.getFieldElement()).isEqualTo(fieldElement);
45 | assertThat(valueResolveContext.getTree()).isEqualTo(fieldTree.getTree());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/resolver/CompositeTypeResolverTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.def.TypeDef;
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.Test;
7 | import org.junit.jupiter.api.extension.ExtendWith;
8 | import org.mockito.Mock;
9 | import org.mockito.junit.jupiter.MockitoExtension;
10 |
11 | import java.util.List;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 | import static org.mockito.Mockito.when;
15 |
16 | @ExtendWith(MockitoExtension.class)
17 | final class CompositeTypeResolverTest {
18 | private @Mock TypeResolver delegate1;
19 | private @Mock TypeResolver delegate2;
20 | private CompositeTypeResolver resolver;
21 |
22 | @BeforeEach
23 | void setup() {
24 | resolver = new CompositeTypeResolver(delegate1, delegate2);
25 | }
26 |
27 | @Test
28 | void callInOrder() {
29 | List input = List.of(ClassDef.builder().qualifiedName("a.A").build());
30 | List out1 = List.of(ClassDef.builder().qualifiedName("a.B").build());
31 | List out2 = List.of(ClassDef.builder().qualifiedName("a.C").build());
32 | when(delegate1.resolve(input)).thenReturn(out1);
33 | when(delegate2.resolve(out1)).thenReturn(out2);
34 |
35 | assertThat(resolver.resolve(input)).isSameAs(out2);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/resolver/SubtypeResolverTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.resolver;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 | import online.sharedtype.processor.domain.def.TypeDef;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import java.util.List;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 |
12 | final class SubtypeResolverTest {
13 | private final SubtypeResolver resolver = new SubtypeResolver();
14 |
15 | @Test
16 | void resolve() {
17 | ClassDef superTypeDef1 = ClassDef.builder().simpleName("SuperClassA").build();
18 | ClassDef superTypeDef2 = ClassDef.builder().simpleName("SuperClassB").build();
19 |
20 | ConcreteTypeInfo superType1 = ConcreteTypeInfo.builder()
21 | .qualifiedName("com.github.cuzfrog.SuperClassA")
22 | .simpleName("SuperClassA")
23 | .typeDef(superTypeDef1)
24 | .build();
25 | ConcreteTypeInfo superType2 = ConcreteTypeInfo.builder()
26 | .qualifiedName("com.github.cuzfrog.SuperClassB")
27 | .simpleName("SuperClassB")
28 | .typeDef(superTypeDef2)
29 | .build();
30 | ClassDef classDef1 = ClassDef.builder()
31 | .simpleName("ClassA")
32 | .qualifiedName("com.github.cuzfrog.ClassA")
33 | .supertypes(List.of(superType1, superType2))
34 | .build();
35 | ClassDef classDef2 = ClassDef.builder()
36 | .simpleName("ClassB")
37 | .qualifiedName("com.github.cuzfrog.ClassB")
38 | .supertypes(List.of(superType1))
39 | .build();
40 |
41 | List input = List.of(classDef1, classDef2);
42 | var res = resolver.resolve(input);
43 | assertThat(res).isEqualTo(input);
44 | assertThat(superTypeDef1.directSubtypes()).containsExactlyInAnyOrder(classDef1, classDef2);
45 | assertThat(superTypeDef2.directSubtypes()).containsExactly(classDef1);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/adaptor/RustHeaderDataAdaptorTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.ContextMocks;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.assertj.core.api.Assertions.assertThat;
7 |
8 |
9 | final class RustHeaderDataAdaptorTest {
10 | private final ContextMocks ctxMocks = new ContextMocks();
11 | private final RustHeaderDataAdaptor adaptor = new RustHeaderDataAdaptor(ctxMocks.getContext());
12 |
13 | @Test
14 | void allowExpr() {
15 | assertThat(adaptor.allowExpr()).containsPattern("#!\\[allow\\([\\w\\s,]+\\)]");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptorTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.adaptor;
2 |
3 | import online.sharedtype.processor.context.ContextMocks;
4 | import org.junit.jupiter.api.Test;
5 | import org.junitpioneer.jupiter.SetSystemProperty;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 |
9 |
10 | final class TypescriptHeaderDataAdaptorTest {
11 | @SetSystemProperty(key = "sharedtype.typescript.custom-code-path", value = "src/test/resources/custom-code.ts")
12 | @Test
13 | void readCustomCodeSnippet() {
14 | ContextMocks ctxMocks = new ContextMocks();
15 | TypescriptHeaderDataAdaptor adaptor = new TypescriptHeaderDataAdaptor(ctxMocks.getContext());
16 | assertThat(adaptor.customCodeSnippet()).isEqualTo("interface A {}" + System.lineSeparator());
17 | }
18 |
19 | @SetSystemProperty(key = "sharedtype.typescript.custom-code-path", value = "not-exists.ts")
20 | @Test
21 | void customCodeSnippetNoFile() {
22 | ContextMocks ctxMocks = new ContextMocks();
23 | TypescriptHeaderDataAdaptor adaptor = new TypescriptHeaderDataAdaptor(ctxMocks.getContext());
24 | assertThat(adaptor.customCodeSnippet()).isEqualTo("");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/converter/ConversionUtilsTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.domain.def.ClassDef;
4 | import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
5 | import online.sharedtype.processor.domain.component.FieldComponentInfo;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import java.util.LinkedHashSet;
9 | import java.util.Set;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | final class ConversionUtilsTest {
14 | @Test
15 | void toSnakeCase() {
16 | assertThat(ConversionUtils.toSnakeCase("camelCase")).isEqualTo("camel_case");
17 | assertThat(ConversionUtils.toSnakeCase("CamelCase")).isEqualTo("camel_case");
18 | assertThat(ConversionUtils.toSnakeCase("camelcase")).isEqualTo("camelcase");
19 | assertThat(ConversionUtils.toSnakeCase("CAMELCASE")).isEqualTo("camelcase");
20 | assertThat(ConversionUtils.toSnakeCase("camelCase123")).isEqualTo("camel_case123");
21 | assertThat(ConversionUtils.toSnakeCase("CamelCase123")).isEqualTo("camel_case123");
22 | assertThat(ConversionUtils.toSnakeCase("camelcase123")).isEqualTo("camelcase123");
23 | assertThat(ConversionUtils.toSnakeCase("CAMELCASE123")).isEqualTo("camelcase123");
24 | }
25 |
26 | @Test
27 | void optionalField() {
28 | var field = FieldComponentInfo.builder().build();
29 | assertThat(ConversionUtils.isOptionalField(field)).isFalse();
30 | field.setOptional(true);
31 | assertThat(ConversionUtils.isOptionalField(field)).isTrue();
32 | }
33 |
34 | @Test
35 | void cyclicReferencedTypeFieldIsOptional() {
36 | var typeDef = ClassDef.builder().build();
37 | var typeInfo = ConcreteTypeInfo.builder().typeDef(typeDef).build();
38 | var field = FieldComponentInfo.builder().type(typeInfo).build();
39 | assertThat(ConversionUtils.isOptionalField(field)).isFalse();
40 |
41 | typeDef.setCyclicReferenced(true);
42 | assertThat(ConversionUtils.isOptionalField(field)).isTrue();
43 | }
44 |
45 | @Test
46 | void buildRustMacroTraitsExpr() {
47 | assertThat(ConversionUtils.buildRustMacroTraitsExpr(Set.of())).isNull();
48 | var macros = new LinkedHashSet(2);
49 | macros.add("A");
50 | macros.add("B");
51 | assertThat(ConversionUtils.buildRustMacroTraitsExpr(macros)).isEqualTo("#[derive(A, B)]");
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/converter/RustMacroTraitsGeneratorImplTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter;
2 |
3 | import online.sharedtype.processor.context.Config;
4 | import online.sharedtype.processor.context.ContextMocks;
5 | import online.sharedtype.processor.context.TestUtils;
6 | import online.sharedtype.processor.domain.def.ClassDef;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.Test;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 | import static org.mockito.Mockito.mock;
12 | import static org.mockito.Mockito.when;
13 |
14 | final class RustMacroTraitsGeneratorImplTest {
15 | private final ContextMocks ctxMocks = new ContextMocks();
16 | private final RustMacroTraitsGenerator generator = new RustMacroTraitsGeneratorImpl(ctxMocks.getContext());
17 |
18 |
19 | private final ClassDef typeDef = ClassDef.builder().build();
20 | private final Config config = mock(Config.class);
21 |
22 | @BeforeEach
23 | void setup() {
24 | when(ctxMocks.getContext().getTypeStore().getConfig(typeDef)).thenReturn(config);
25 | }
26 |
27 | @Test
28 | void genMacroTraits() {
29 | var anno = TestUtils.defaultSharedTypeAnnotation();
30 | when(config.getAnno()).thenReturn(anno);
31 | when(anno.rustMacroTraits()).thenReturn(new String[]{"PartialEq", "Clone"});
32 | assertThat(generator.generate(typeDef)).containsExactly("Debug", "PartialEq", "Clone");
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/converter/type/GoTypeExpressionConverterTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.converter.type;
2 |
3 | import online.sharedtype.SharedType;
4 | import online.sharedtype.processor.context.Config;
5 | import online.sharedtype.processor.context.ContextMocks;
6 | import online.sharedtype.processor.domain.Constants;
7 | import online.sharedtype.processor.domain.def.ClassDef;
8 | import online.sharedtype.processor.domain.type.ArrayTypeInfo;
9 | import online.sharedtype.processor.domain.type.DateTimeInfo;
10 | import org.junit.jupiter.api.Test;
11 |
12 | import static org.assertj.core.api.Assertions.assertThat;
13 | import static org.mockito.Mockito.mock;
14 | import static org.mockito.Mockito.when;
15 |
16 | final class GoTypeExpressionConverterTest {
17 | private final ContextMocks ctxMocks = new ContextMocks();
18 | private final GoTypeExpressionConverter converter = new GoTypeExpressionConverter(ctxMocks.getContext());
19 |
20 | private final Config config = mock(Config.class);
21 | private final ClassDef contextTypeDef = ClassDef.builder().simpleName("Abc").build();
22 |
23 | @Test
24 | void typeContract() {
25 | assertThat(converter.typeNameMappings.keySet()).containsAll(Constants.LITERAL_TYPES);
26 | }
27 |
28 | @Test
29 | void convertArrayType() {
30 | String expr = converter.toTypeExpr(new ArrayTypeInfo(Constants.INT_TYPE_INFO), contextTypeDef);
31 | assertThat(expr).isEqualTo("[]int32");
32 | }
33 |
34 | @Test
35 | void convertObjectType() {
36 | assertThat(converter.toTypeExpr(Constants.OBJECT_TYPE_INFO, contextTypeDef)).isEqualTo("any");
37 | }
38 |
39 | @Test
40 | void convertDateTimeAndTypeMappings() {
41 | when(config.getGoTargetDatetimeTypeLiteral()).thenReturn("DefaultDateLiteral");
42 | DateTimeInfo dateTimeInfo = new DateTimeInfo("a.b.A2");
43 | assertThat(converter.dateTimeTypeExpr(dateTimeInfo, config)).isEqualTo("DefaultDateLiteral");
44 | dateTimeInfo.addMappedName(SharedType.TargetType.GO, "BBB");
45 | assertThat(converter.dateTimeTypeExpr(dateTimeInfo, config)).isEqualTo("BBB");
46 | }
47 |
48 | @Test
49 | void convertPrimitiveType() {
50 | assertThat(converter.toTypeExpr(Constants.INT_TYPE_INFO, contextTypeDef)).isEqualTo("int32");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/processor/src/test/java/online/sharedtype/processor/writer/render/MustacheTemplateRendererTest.java:
--------------------------------------------------------------------------------
1 | package online.sharedtype.processor.writer.render;
2 |
3 | import com.github.mustachejava.Mustache;
4 | import com.github.mustachejava.MustacheFactory;
5 | import online.sharedtype.SharedType;
6 | import online.sharedtype.processor.support.utils.Tuple;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.api.extension.ExtendWith;
10 | import org.mockito.Mock;
11 | import org.mockito.junit.jupiter.MockitoExtension;
12 |
13 | import java.io.Writer;
14 | import java.util.Collections;
15 | import java.util.HashMap;
16 |
17 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
18 | import static org.mockito.Mockito.verify;
19 | import static org.mockito.Mockito.when;
20 |
21 | @ExtendWith(MockitoExtension.class)
22 | final class MustacheTemplateRendererTest {
23 | private @Mock MustacheFactory mf;
24 | private MustacheTemplateRenderer renderer;
25 |
26 | private @Mock Mustache compiledMustache;
27 | private @Mock Writer writer;
28 | private final Template template = new Template(SharedType.TargetType.GO, "test");
29 |
30 | @BeforeEach
31 | void setUp() {
32 | renderer = new MustacheTemplateRenderer(mf);
33 | }
34 |
35 | @Test
36 | void loadTemplatesAndRender() {
37 | when(mf.compile("templates/go/test.mustache")).thenReturn(compiledMustache);
38 |
39 | renderer.render(writer, Collections.singletonList(Tuple.of(template, new HashMap<>())));
40 | verify(compiledMustache).execute(writer, new HashMap<>());
41 | }
42 |
43 | @Test
44 | void errorIfTemplateNotLoaded() {
45 | assertThatThrownBy(() -> renderer.render(writer, Collections.singletonList(Tuple.of(template, new HashMap<>()))))
46 | .hasMessageContaining("Template not found");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/processor/src/test/resources/custom-code.ts:
--------------------------------------------------------------------------------
1 | interface A {}
2 |
--------------------------------------------------------------------------------
/processor/src/test/resources/test-sharedtype-type-mappings.properties:
--------------------------------------------------------------------------------
1 | sharedtype.typescript.type-mappings=MyType1:RenamedType1,\
2 | MyType2:RenamedType2
3 |
--------------------------------------------------------------------------------
/processor/src/test/resources/test-sharedtype-user.properties:
--------------------------------------------------------------------------------
1 | sharedtype.targets=TYPESCRIPT, CONSOLE,
2 |
3 | sharedtype.optional-annotations=a.b.TsOptional
4 |
5 | sharedtype.typescript.java-object-map-type=unknown
6 |
--------------------------------------------------------------------------------
/processor/src/test/resources/test-sharedtype-wrong-target.properties:
--------------------------------------------------------------------------------
1 | sharedtype.targets=ENGLISH,
2 |
--------------------------------------------------------------------------------
/processor/src/test/resources/test-sharedtype-wrong-ts-optional-field-format.properties:
--------------------------------------------------------------------------------
1 | sharedtype.typescript.optional-field-format=abc,?
2 |
--------------------------------------------------------------------------------
/setenv:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | version=$1
3 |
4 | if [ -z "$version" ];then
5 | version="21"
6 | echo "No version provided, use default jdk$version"
7 | fi
8 |
9 | if [[ "$version" == "17" ]];then
10 | export JAVA_HOME=$JAVA17_HOME
11 | elif [[ "$version" == "8" ]];then
12 | export JAVA_HOME=$JAVA8_HOME
13 | elif [[ "$version" == "11" ]];then
14 | export JAVA_HOME=$JAVA11_HOME
15 | elif [[ "$version" == "21" ]];then
16 | export JAVA_HOME=$JAVA21_HOME
17 | else
18 | echo "Unsupported version $version"
19 | return
20 | fi
21 |
22 | export MAVEN_OPTS="-Xmx512m -Xms512m"
23 | export PATH=$JAVA_HOME/bin:$MVND_HOME/bin:$PATH
24 |
25 | if [ -z "$MVND_HOME" ];then
26 | echo "MVND_HOME is not set, mvnd is recommended for local development, see https://github.com/apache/maven-mvnd"
27 | java -version
28 | else
29 | mvnd -version
30 | fi
31 |
--------------------------------------------------------------------------------