Skip to content

Commit e1fdf9a

Browse files
committed
proto의 optional을 처리할 수 있도록한다
1 parent 7e6f825 commit e1fdf9a

File tree

14 files changed

+337
-69
lines changed

14 files changed

+337
-69
lines changed

Dockerfile

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
FROM ubuntu:22.04
22

3-
# Install dependencies
43
RUN apt-get update && apt-get install -y \
54
curl \
65
unzip \
@@ -9,20 +8,17 @@ RUN apt-get update && apt-get install -y \
98
make \
109
&& apt-get clean
1110

12-
# Set working directory
1311
WORKDIR /workspace
1412

15-
# Copy project
1613
COPY . .
1714

18-
# Make Gradle wrapper executable
1915
RUN chmod +x ./gradlew
20-
21-
# Build the plugin
2216
RUN ./gradlew :protoc-plugin:installDist
2317

24-
# Set environment variables
18+
# ➡플러그인 실행 스크립트 추가
19+
RUN echo '#!/bin/bash\nexec java -cp "/workspace/protoc-plugin/build/install/protoc-plugin/lib/*" io.github.protogenerator.plugin.StrictProtoGenerator' > /workspace/scripts/protoc-gen-strictproto && \
20+
chmod +x /workspace/scripts/protoc-gen-strictproto
21+
2522
ENV PATH="/workspace/protoc-plugin/build/install/protoc-plugin/bin:$PATH"
2623

27-
# Default shell
2824
CMD ["bash"]

Makefile

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ DOCKER_COMPOSE=docker compose
22
SERVICE_NAME=strictproto-dev
33

44
build:
5-
$(DOCKER_COMPOSE) build
5+
$(DOCKER_COMPOSE) build --no-cache
66

77
up:
88
$(DOCKER_COMPOSE) up -d
@@ -13,12 +13,10 @@ down:
1313
bash:
1414
$(DOCKER_COMPOSE) exec $(SERVICE_NAME) bash
1515

16-
example-protoc-run:
17-
$(DOCKER_COMPOSE) run --rm $(SERVICE_NAME) bash -c "\
18-
mkdir -p /workspace/generated && \
19-
chmod +x scripts/protoc-gen-strictproto && \
20-
protoc \
21-
--plugin=protoc-gen-strictproto=/workspace/scripts/protoc-gen-strictproto \
22-
--strictproto_out=/workspace/generated \
23-
--proto_path=/workspace/examples \
24-
/workspace/examples/Member.proto"
16+
generate-strictproto:
17+
$(DOCKER_COMPOSE) run --rm $(SERVICE_NAME) bash scripts/generate-strictproto.sh
18+
19+
generate-java:
20+
$(DOCKER_COMPOSE) run --rm $(SERVICE_NAME) bash scripts/generate-java.sh
21+
22+
generate-all: generate-strictproto generate-java

generator-core/build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,13 @@ repositories {
1111
}
1212

1313
dependencies {
14-
implementation("com.google.protobuf:protobuf-java:4.28.2")
14+
implementation("com.google.protobuf:protobuf-java:3.25.3")
15+
}
16+
17+
sourceSets {
18+
main {
19+
java {
20+
srcDirs("src/main/java", "../generated-java", "../generated")
21+
}
22+
}
1523
}

generator-core/src/main/java/io/github/protogenerator/core/ConstructorHelperGenerator.java

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,88 @@
22

33
import com.google.protobuf.DescriptorProtos;
44

5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
9+
510
public class ConstructorHelperGenerator {
6-
public String generateConstructorHelper(String protoPackage, String originalClassName, DescriptorProtos.DescriptorProto message) {
7-
StringBuilder sb = new StringBuilder();
11+
public String generateConstructorHelper(String protoPackage, String originalClassName, DescriptorProtos.DescriptorProto descriptorProto) {
12+
StringBuilder builder = new StringBuilder();
13+
String packageDecl = protoPackage.isEmpty() ? "" : "package " + protoPackage + ";\n\n";
14+
15+
builder.append(packageDecl);
16+
builder.append(generateImports(descriptorProto, protoPackage));
17+
builder.append("public final class ")
18+
.append(originalClassName)
19+
.append("Constructor {\n\n")
20+
.append(" private ")
21+
.append(originalClassName)
22+
.append("Constructor() {}\n\n")
23+
.append(" public static ")
24+
.append(originalClassName)
25+
.append(" from(");
826

9-
sb.append("package ").append(protoPackage).append(";\n\n");
10-
sb.append("public final class ").append(originalClassName).append("Constructor {\n\n");
27+
for (int i = 0; i < descriptorProto.getFieldCount(); i++) {
28+
DescriptorProtos.FieldDescriptorProto field = descriptorProto.getField(i);
29+
if (i > 0) builder.append(", ");
30+
builder.append(JavaTypeMapper.map(field)).append(" ").append(field.getName());
31+
}
1132

12-
sb.append(" private ").append(originalClassName).append("Constructor() {}\n\n");
33+
builder.append(") {\n")
34+
.append(" ")
35+
.append(originalClassName)
36+
.append(".Builder builder = ")
37+
.append(originalClassName)
38+
.append(".newBuilder();\n");
1339

14-
sb.append(" public static ").append(originalClassName).append(" from(");
15-
for (int i = 0; i < message.getFieldCount(); i++) {
16-
DescriptorProtos.FieldDescriptorProto field = message.getField(i);
17-
sb.append(JavaTypeMapper.map(field.getType())).append(" ").append(field.getName());
18-
if (i < message.getFieldCount() - 1) {
19-
sb.append(", ");
40+
for (DescriptorProtos.FieldDescriptorProto field : descriptorProto.getFieldList()) {
41+
String fieldName = field.getName();
42+
String setter = "set" + StringUtil.capitalize(fieldName);
43+
if (field.hasProto3Optional()) {
44+
builder.append(" if (")
45+
.append(fieldName)
46+
.append(" != null) {\n")
47+
.append(" builder.")
48+
.append(setter)
49+
.append("(")
50+
.append(fieldName)
51+
.append(");\n")
52+
.append(" }\n");
53+
} else {
54+
builder.append(" builder.")
55+
.append(setter)
56+
.append("(")
57+
.append(fieldName)
58+
.append(");\n");
2059
}
2160
}
22-
sb.append(") {\n");
2361

24-
sb.append(" return ").append(originalClassName).append(".newBuilder()\n");
25-
for (DescriptorProtos.FieldDescriptorProto field : message.getFieldList()) {
26-
sb.append(" .set").append(StringUtil.capitalize(field.getName())).append("(").append(field.getName()).append(")\n");
27-
}
28-
sb.append(" .build();\n");
29-
sb.append(" }\n");
62+
builder.append(" return builder.build();\n")
63+
.append(" }\n")
64+
.append("}\n");
65+
66+
return builder.toString();
67+
}
3068

31-
sb.append("}\n");
69+
private String generateImports(DescriptorProtos.DescriptorProto descriptorProto, String packageName) {
70+
Set<String> imports = new HashSet<>();
71+
72+
for (DescriptorProtos.FieldDescriptorProto field : descriptorProto.getFieldList()) {
73+
DescriptorProtos.FieldDescriptorProto.Type type = field.getType();
74+
if (type == DescriptorProtos.FieldDescriptorProto.Type.TYPE_MESSAGE ||
75+
type == DescriptorProtos.FieldDescriptorProto.Type.TYPE_ENUM) {
76+
77+
String fieldType = JavaTypeMapper.map(field);
78+
79+
if (!List.of("int", "long", "float", "double", "boolean", "String", "Object").contains(fieldType)) {
80+
imports.add("import " + packageName + "." + fieldType + ";");
81+
}
82+
}
83+
}
3284

33-
return sb.toString();
85+
return imports.stream()
86+
.sorted()
87+
.collect(Collectors.joining("\n")) + "\n\n";
3488
}
3589
}

generator-core/src/main/java/io/github/protogenerator/core/JavaTypeMapper.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import com.google.protobuf.DescriptorProtos;
44

55
public class JavaTypeMapper {
6-
public static String map(DescriptorProtos.FieldDescriptorProto.Type type) {
7-
return switch (type) {
6+
public static String map(DescriptorProtos.FieldDescriptorProto field) {
7+
boolean isOptional = field.hasProto3Optional();
8+
return switch (field.getType()) {
89
case TYPE_STRING -> "String";
9-
case TYPE_INT32 -> "int";
10-
case TYPE_INT64 -> "long";
11-
case TYPE_BOOL -> "boolean";
12-
case TYPE_FLOAT -> "float";
13-
case TYPE_DOUBLE -> "double";
10+
case TYPE_INT32 -> isOptional ? "Integer" : "int";
11+
case TYPE_INT64 -> isOptional ? "Long" : "long";
12+
case TYPE_BOOL -> isOptional ? "Boolean" : "boolean";
13+
case TYPE_FLOAT -> isOptional ? "Float" : "float";
14+
case TYPE_DOUBLE -> isOptional ? "Double" : "double";
15+
case TYPE_ENUM -> field.getTypeName().substring(field.getTypeName().lastIndexOf('.') + 1); // Enum은 그대로 이름 사용
16+
case TYPE_MESSAGE -> field.getTypeName().substring(field.getTypeName().lastIndexOf('.') + 1); // 메시지도 이름 사용
1417
default -> "Object";
1518
};
1619
}

protoc-plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ repositories {
1616

1717
dependencies {
1818
implementation(project(":generator-core"))
19-
implementation("com.google.protobuf:protobuf-java:4.28.2")
19+
implementation("com.google.protobuf:protobuf-java:3.25.3")
2020
}

protoc-plugin/src/main/java/io/github/protogenerator/plugin/StrictProtoGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public static void main(String[] args) throws IOException {
1717
public static PluginProtos.CodeGeneratorResponse process(PluginProtos.CodeGeneratorRequest request) {
1818
ConstructorHelperGenerator generator = new ConstructorHelperGenerator();
1919
PluginProtos.CodeGeneratorResponse.Builder responseBuilder = PluginProtos.CodeGeneratorResponse.newBuilder();
20+
responseBuilder.setSupportedFeatures(PluginProtos.CodeGeneratorResponse.Feature.FEATURE_PROTO3_OPTIONAL_VALUE);
2021

2122
List<DescriptorProtos.FileDescriptorProto> protoFiles = request.getProtoFileList();
2223
for (DescriptorProtos.FileDescriptorProto file : protoFiles) {

scripts/generate-java.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/bash
2+
set -e
3+
4+
PROTO_DIR=examples
5+
OUT_DIR=/workspace/generated-java
6+
7+
mkdir -p ${OUT_DIR}
8+
cd /workspace
9+
10+
for file in ${PROTO_DIR}/*.proto; do
11+
protoc \
12+
--experimental_allow_proto3_optional \
13+
--proto_path=/workspace \
14+
--java_out=${OUT_DIR} \
15+
${file}
16+
done

scripts/generate-strictproto.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
set -e
3+
4+
PROTO_DIR=examples
5+
OUT_DIR=/workspace/generated
6+
7+
mkdir -p ${OUT_DIR}
8+
cd /workspace
9+
10+
for file in ${PROTO_DIR}/*.proto; do
11+
echo "Generating constructor for ${file}..."
12+
protoc \
13+
--experimental_allow_proto3_optional \
14+
--plugin=protoc-gen-strictproto=/workspace/scripts/protoc-gen-strictproto \
15+
--strictproto_out=${OUT_DIR} \
16+
--proto_path=${PROTO_DIR} \
17+
${file}
18+
done
19+
20+
#!/bin/bash
21+
set -e
22+
23+
PROTO_DIR=examples
24+
OUT_DIR=/workspace/generated
25+
26+
mkdir -p ${OUT_DIR}
27+
cd /workspace
28+
29+
for file in ${PROTO_DIR}/*.proto; do
30+
protoc \
31+
--experimental_allow_proto3_optional \
32+
--plugin=protoc-gen-strictproto=/workspace/scripts/protoc-gen-strictproto \
33+
--strictproto_out=/workspace/generated \
34+
--proto_path=/workspace/examples \
35+
/workspace/examples/Payment.proto
36+
done

scripts/protoc-gen-strictproto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
java -cp "/workspace/protoc-plugin/build/install/protoc-plugin/lib/*" io.github.protogenerator.plugin.StrictProtoGenerator
2+
exec java -cp "/workspace/protoc-plugin/build/install/protoc-plugin/lib/*" io.github.protogenerator.plugin.StrictProtoGenerator

0 commit comments

Comments
 (0)